Улучшите читаемость кода с помощью JSDoc

JavaScript

Проработал более четырех лет, в основном занимаясь JavaScript.
Я написал много кода и прочитал много кода Я искренне считаю, что писать код, который другие не могут понять, — это не навык, а писать код, понятный всем, — это действительно здорово.
Как мы все знаем, JavaScript — это скриптовый язык со слабой типизацией, а это означает, что не интуитивно понятно, какова функция этого кода из редактора, а некоторые вещи можно определить только во время фактического выполнения кода.
Поэтому, чтобы решить проблему высокой стоимости обслуживания JavaScript в масштабных проектах, наша команда некоторое время назад начала использовать TypeScript, но код, накопленный в предыдущие годы, не означает, что все изменения можно выполнить сразу, поэтому это рефакторинг будет долгим процессом.
При рефакторинге нам по-прежнему необходимо продолжать поддерживать исходный проект JavaScript, а JSDoc оказывается промежуточным решением, которое позволяет нам уменьшить сложность обслуживания проектов JavaScript и улучшить читаемость в виде комментариев.

эффект

Я использую редактор кода vs, который имеет встроенную поддержку jsdoc, а также выводит соответствующий тип на основе некоторых констант и синтаксиса.
Очень удобно видеть эффект в редакторе, поэтому все приведенные ниже примеры основаны на vscode.

Во-первых, JSDoc никак не влияет на исходный код, весь контент пишется в комментариях.
Так что вам не нужно беспокоиться о том, как JSDoc негативно повлияет на вашу программу.

Вы можете сначала посмотреть на эффект отображения обычного файла JavaScript в редакторе:

Очевидно, что редактор также не может определить, что означает эта функция, потому что можно добавить два параметра любого типа.
Таким образом, редактор будет использовать тип, который часто используется в TypeScript для идентификации любого типа.anyКлючевые слова для описания параметров функции и возвращаемого значения.

В этом случае мы можем просто использовать JSDoc, чтобы вручную описать функцию этой функции:

На самом деле некоторые функции нужно указывать вручную@return {TYPE}Чтобы определить тип возвращаемого значения функции, но поскольку функция нашей функции заключается в добавлении и возврате двух параметров, редактор определяет тип возвращаемого значения функции.

Сравнивая верхний и нижний фрагменты кода, разницы в коде нет, некоторые люди могут насмехаться над этим, думая, что код достаточно ясен и не требует дополнительных пояснений.
Такого рода слепая уверенность обычно разрушается после того, как я перенял худший код других людей, а затем подумал о том, что я сделал неправильно и что мне нужно поддерживать такой код.

Или придумаем немного более сложный пример:

Вроде бы четкий и лаконичный пример, в котором вообще не видно ничего плохогокроме двух асинхронныхawaitможно объединить в один.
Действительно, если этот код пролежал в проекте без изменения требований, то можно сказать, что этот код существует идеально.
Если этот код поддерживается автором, который его написал, нет риска, что этот код будет поддерживаться.

Однако, если этот код раздается, измените другие маленькие партнеры для обслуживания.
Тогда у него могут возникнуть следующие вопросы:

  1. getUserInfoКакова структура возвращаемого значения
  2. createOrderКакова структура возвращаемого значения
  3. notifyКакие две переменные передаются в

Мы можем толькоnotifyНайдите некоторые подсказки в функции и посмотрите некоторые свойства объектов, возвращаемых первыми двумя функциями,Но все еще не могу знать, какие типы этих свойств.
Если вы хотите поддерживать такой фрагмент кода, вам нужно потратить много ресурсов мозга на его запоминание, что на самом деле является очень рентабельным.Когда этот код передается третьему лицу, третьему лицу нужно идти через полный процесс, одну функцию, одну строку кода для чтения и запоминания.
Если вы примете это за глубокое понимание программы и хорошее понимание дела, то я не думаю, что смогу вам помочь. Так же, как и при кассе в супермаркете, ни один кассир не будет гордиться тем, что может запомнить цены более чем на N товаров.Зачем вам браться за голову за то, что может сделать сканер штрих-кода?

Основное использование

Как упоминалось выше, JSDoc — это контент определенного формата, написанный в комментариях.
Большая часть разметки в файлах JavaScript выполняется на уровне блоков, т. е. с использованием/** XXX */чтобы определить его, но вы также можете написать его в коде, если хотите.

JSDoc предоставляет множество разметки для различных сценариев.
Но не все из них являются общеупотребительными (а после использования vscode многие теги, которые нужно указать вручную, редактор может сделать это за вас), часто используемые не более чем следующие:

  • @type определяет тип переменной
  • @param определяет тип и описание параметра функции.
  • @return определяет тип и описание возвращаемого значения функции.

Полный список можно найти здесьBlock tags

В принципе, после использования трех вышеуказанных маркеров большинство проблем было решено.
У JSDoc есть определенные требования в плане написания, например, он тоже должен иметь такую ​​структуру в строке/** XXX */,если/* XXX */будет игнорироваться.
Чаще используется метод многострочного написания, в vscode можно печатать прямо над функцией/**Затем нажмите Enter, редактор автоматически заполнит большую часть содержимого, включая типы параметров, описания параметров и зарезервированные позиции для описаний функций.TABключ для быстрого переключения.

Фактически@typeЧастота использования очень низкая по сравнению с двумя другими, потому что в большинстве случаев@typeИспользуется для идентификации типа переменной.
В основном есть только два источника переменных: 1. Присвоение базового типа 2. Возвращаемое значение функции Первый — это присвоение первого базового типа, которое в основном делается vscode за вас, без необходимости вручную указывать его самостоятельно.
И возвращаемое значение другой функции, если мы добавим к функции@return, то тип переменной, которая вызывает функцию и получает возвращаемое значение, также будет установлен на@returnсоответствующий тип.

type

Однако, поскольку в двух других тегах есть спецификации, связанные с типом, давайте возьмем @type для иллюстрации.

Во-первых, в JSDoc поддерживаются все основные типы, включая числа, строки, логические значения и тому подобное.

/** @type {number} */
/** @type {string} */
/** @type {boolean} */
/** @type {RegExp} */

// 或者是一个函数
/** @type {function} */

// 一个包含参数的函数
/** @type {function(number, string)} */

// Object结构的参数
/** @type {function({ arg1: number, arg2: string })} */

// 一个包涵参数和返回值的函数
/** @type {function(number, string): boolean} */

Введите приведенные выше комментарии в vscode, и вы легко получите динамические подсказки.
Конечно, что касается функций, рекомендуется использовать @param и @return для достижения лучших результатов.

Расширенные сложные типы

Приведенные выше примеры в основном основаны на описании базовых типов, но в реальном процессе разработки не будет сказано, что вы можете использовать только такие базовые типы.
Там должно быть большое количество сложных структурных типов переменных, параметров или возвратных значений.

Что касается параметров функций, сложные типы могут быть описаны в JSDoc двумя способами:

Однако это может применяться только к@param, а возможность повторного использования не высока, если есть несколько определений одной и той же структуры, то такие аннотации нужно копировать несколько раз, что явно не является элегантным способом написания.
Или мы можем использовать два других тега,@typedefа также@property, формат аналогичен разметке, упомянутой выше, и может применяться во всех местах, где необходимо указать тип:

использовать@typedefОпределенные типы можно легко использовать повторно, и мы можем напрямую указать наши определенные типы, где это необходимо.
Точно так же такой пользовательский тип может быть непосредственно применен к@returnсередина.

param

Это относительно важная метка, используемая для обозначения соответствующей информации о параметрах функции.
Конкретный формат выглядит следующим образом (после перехода на TypeScript определение типа обычно удаляется и вместо него используется определение типа в коде):

/**
 * @param {number} param 描述
 */
function test (param) { }

// 或者可以结合着 @type 来写(虽说很少会这么写)

/**
 * @param param 描述
 */
function test (/** @type number */ param) { }

необязательный параметр

Если мы хотим указать параметр как необязательный параметр, вы можете упаковать его в имя параметра.[]Вот и все.

/**
 * @param {number} [param] 描述
 */
function test (param) { }

Коллеги также упомянули о прописывании значений по умолчанию в документе.По сути, если ваши необязательные параметры уже имеют значения по умолчанию в битах параметров, то никаких дополнительных дополнений не требуется.[]указать, vscode поможет вам отметить.

// 文档中提到的默认值写法
/**
 * @param {number} [param=123] 描述
 */
function test (param = 123) { }

// 而实际上使用 vscode 以后就可以简化为
/**
 * @param param 描述
 */
function test (param = 123) { }

Эффект от обоих одинаковый, и поскольку мы вручную указываем значение базового типа, мы можем даже опустить спецификацию типа и просто определить описание параметра.

return

Этот тег используется для указания возвращаемого функцией значения. Использование такое же, как@paramтип, и в основном оба они появляются одновременно, с@paramРазница в том, что потому@returnБудет только один, поэтому нет необходимости указывать имя параметра, как в предыдущем случае.

/**
 * @return {number} 描述
 */
function test () { }

Обработка возвращаемого значения типа Promise

В наши дни в основномPromiseОн стал популярным, поэтому возвращаемое значение многих функций может быть не результатом, аPromise.
Итак, в vscode, на основеPromiseиспользовать@return, есть два способа написать это:

// 函数返回 Promise 实例的情况可以这么指定类型
/**
 * @return {Promise<number>}
 */
function test () {
  return new Promise((res) => {
    res(1)
  })
}

// 或者使用 async 函数定义的情况下可以省略 @return 的声明
async function test () {
  return 1
}

  // 如果返回值是一个其他定义了类型的函数 or 变量,那么效果一样
async function test () {
  return returnVal()
}

/** @return {string} */
function returnVal () {}

резюме

Возвращаясь к нашему исходному фрагменту кода, измените его, чтобы он выглядел так, как добавлена ​​версия JSDoc:

/**
 * @typedef   {Object} UserInfo
 * @property  {number} uid  用户UID
 * @property  {string} name 昵称
 * 
 * @typedef   {Object} Order
 * @property  {number} orderId 订单ID
 * @property  {number} price   订单价格
 */
async function main () {
  const uid = 1

  const orders = await createOrder(uid)

  const userInfo = await getUserInfo(uid)

  await notify(userInfo, orders)
}

/**
 * 获取用户信息
 * @param   {number} uid 用户UID
 * @return  {Promise<UserInfo>}
 */
async function getUserInfo (uid) { }

/**
 * 创建订单
 * @param  {number} uid 用户UID
 * @return {Promise<Order>}
 */
async function createOrder (uid) { }

/**
 * 发送通知
 * @param {UserInfo} userInfo 
 * @param {Order}    orders 
 */
async function notify (userInfo, orders) { }

На самом деле, несколько строк текста не добавляются.До перехода на TypeScript использование JSDoc может в определенной степени снизить затраты на обслуживание, особенно после использования vscode, на самом деле не так много комментариев, которые нужно писать вручную.
Но преимущество в том, что сопровождающий может четко видеть функцию функции и тип переменной. Код — это документация.
И в ежедневной разработке, в сочетании с функциями автоматического завершения и динамических подсказок редактора, он должен быть в состоянии улучшить опыт разработки.

Выше приведены лишь несколько тегов, обычно используемых в JSDoc. На самом деле есть еще функции, которые не были упомянуты. Конкретный адрес документа:jsdoc

использованная литература