Отладка кода без комментариев — это как быть детективом, пытающимся раскрыть преступление, но единственный свидетель говорит на иностранном языке, а какие-то влиятельные люди (клиенты) дышат тебе в затылок, чтобы вчера раскрыть дело.

источник изображения — Двое мужчин смотрят на ноутбук · Бесплатная стоковая фотография (pexels.com)

Введение

Добавление комментариев к коду важно по нескольким причинам:

1. Читаемость кода. Комментарии помогают разработчикам понять назначение кода, объясняя его простым языком, облегчая его чтение и изменение.

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

3. Совместная работа. Комментарии предотвращают ошибки и неверные толкования в совместных проектах, предоставляя контекст для изменений кода, гарантируя, что все находятся на одной странице.

4. Документация. Комментарии к коду проясняют назначение и функциональность, обеспечивая соответствие отраслевым стандартам и рекомендациям. Они делают код более читабельным, удобным в сопровождении и с ним проще работать, как инструкции LEGO по сборке набора.

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

Ментальная модель добавления комментариев к коду

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

1. Цель. Объясните назначение кода, какую проблему он решает и почему он существует.

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

3. Вывод. Укажите вывод, создаваемый кодом, включая его тип и все необходимые сведения.

4. Использование. Приведите примеры использования кода, включая соответствующие фрагменты кода.

5. Ссылки. Добавьте ссылки на связанный код и внешние ресурсы.

6. Рекомендации. Выделите любые рекомендации или ошибки, связанные с кодом.

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

Согласованность и стандартизация

Крайне важно поддерживать согласованность форматирования и стиля комментариев во всей кодовой базе. Хорошим вариантом является использование такого инструмента, как JS Doc (документация по JavaScript), который представляет собой набор стандартов документации и инструментов для документирования кода JavaScript.

Ниже приведены некоторые функции JS Doc:

  • Он использует теги для сбора информации о структуре кода, функциях, параметрах, возвращаемых значениях и других деталях, чтобы создать удобочитаемый справочник для других разработчиков.
  • Тег JS Doc начинается с символа @, за которым следует имя тега и любая соответствующая информация. Например, тег param используется для описания параметра функции, а тег returns используется для описания возвращаемого значения функции.
  • Он может генерировать документацию в HTML или других форматах из встроенных комментариев в исходном коде.

Давайте подробно рассмотрим некоторые из наиболее часто используемых тегов JS Doc. Для справки, мы добавим комментарии к следующему коду JS:

function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

Не беспокойтесь, если неясно, что делает код. В следующих разделах будет дано объяснение.

Часто используемые теги документов JS

Давайте взглянем на пару тегов JS Doc, которые используются довольно часто.

  • @todo

Прежде чем что-то делать, важно признать, что это необходимо сделать и что это приносит пользу системе. @todotag делает то же самое.

/**
 * @todo Add details about the factorial calculation and edge cases.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @todo Add an example usage of the callback function.
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}
  • @description

Первый шаг — описать, что делает код. Тег @description используется для предоставления подробного описания функции, объекта или свойства. Посмотрим, как выглядит код после добавления этого тега.

/**
 * @description A function that calculates the factorial of a number using recursion.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}
  • @param

А вот и та часть, где сообщается, какие входные данные принимает код. Тег @param используется для описания параметров функции. Добавление его в пример делает код таким:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}
  • @returns

Пока мы знаем, что делает функция, и какие входные данные она принимает. Далее идет описание возвращаемого значения функции. Добавление @returns к примеру делает код таким:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}
  • @throws

Будут моменты, когда функция будет сталкиваться с ошибками и не сможет дать желаемых результатов. @throws помогает документировать эти сценарии. Добавление @throwsк примеру делает код таким:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}
  • @example

Иногда потребитель или инициатор кода не заинтересован в деталях низкоуровневой реализации и озабочен вызовом и получением ожидаемых результатов. @exampletag используется для предоставления примера использования функции или объекта. Добавление @exampleк примеру делает код таким:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 * 
 * @example
 * factorial(5);
 * // returns 120
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 * 
 * @example
 * factorialWithCallback(5, (error, result) => {
 *   if (error) {
 *     console.error(error);
 *   } else {
 *     console.log(result);
 *   }
 * });
 * // logs { input: 5, output: 120 }
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}
  • @type , @typedef и @property

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

С другой стороны, тег @typedefJSDoc используется для определения пользовательского типа. Это может быть полезно в сложных проектах, где у вас есть пользовательские структуры данных или типы, которые необходимо задокументировать. Затем на пользовательский тип, определенный с помощью @typedef, можно ссылаться с помощью тега @typeв других частях кода.

По сути, @typedefиспользуется для определения пользовательского типа, а @typeиспользуется для указания типа объекта.

Тег @propertyJSDoc используется для документирования свойств объекта или класса. Обычно используется в сочетании с @typedef для документирования структуры объекта или класса. @propertytag указывает имя и тип свойства, а также описание его назначения.

Добавление @type, @typedef и @propertyк примеру делает код таким:

/**
 * @typedef {Object} FactorialResult
 * 
 * @property {number} input - The input to the factorial function.
 * @property {number} output - The output of the factorial function.
 */

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 * 
 * @example
 * factorial(5);
 * // returns 120
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 * 
 * @example
 * factorialWithCallback(5, (error, result) => {
 *   if (error) {
 *     console.error(error);
 *   } else {
 *     console.log(result);
 *   }
 * });
 * // logs { input: 5, output: 120 }
 */
function factorialWithCallback(n, callback) {
  /**
   * @type {FactorialResult}
   */
  const result = {
    input: n,
    output: factorial(n),
  };
  try {
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

Важно отметить разницу между тегами @paramи @property. @propertytag используется для описания свойств или атрибутов объекта или класса, тогда как @paramtag используется для описания параметров функции или метода. В отличие от тега @propertyтег @param используется внутри блока JSDocs функции или метода, а не за его пределами.

  • @callback

Он используется для описания функции обратного вызова. @callbacktag определяет параметры функции обратного вызова и их типы. Добавление тега @callback в пример делает код таким:

/**
 * @typedef {Object} FactorialResult
 * 
 * @property {number} input - The input to the factorial function.
 * @property {number} output - The output of the factorial function.
 */

/**
 * @callback FactorialCallback
 * 
 * @param {Error} error - The error that occurred during the calculation, if any.
 * @param {FactorialResult} result - The result of the calculation.
 */

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 * 
 * @example
 * factorial(5);
 * // returns 120
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {FactorialCallback} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 * 
 * @example
 * factorialWithCallback(5, (error, result) => {
 *   if (error) {
 *     console.error(error);
 *   } else {
 *     console.log(result);
 *   }
 * });
 * // logs { input: 5, output: 120 }
 */
function factorialWithCallback(n, callback) {
  /**
   * @type {FactorialResult}
   */
  const result = {
    input: n,
    output: factorial(n),
  };
  try {
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

В этом коде тег @callback используется для определения пользовательского типа FactorialCallback, который представляет собой функцию обратного вызова, передаваемую в качестве параметра функции factorialCallback.

Затем на тип FactorialCallback ссылаются в @paramtag для параметра обратного вызова функции factorialWithCallback.

Пример/демонстрация

Предварительный просмотр приведенного выше фрагмента и документации, которую он генерирует с помощью JS Doc, можно проверить здесь.

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

Краткое содержание

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

Независимо от того, являетесь ли вы опытным разработчиком или только начинаете, потратьте время на стандартизированное документирование своего кода. Это не только принесет пользу продукту в краткосрочной перспективе, но и обеспечит долговечность и ремонтопригодность кодовой базы на долгие годы.

Я бы посоветовал вам использовать эти системы в своих проектах. Поверьте, ваше будущее «я» скажет вам за это спасибо! 😊