Die subtile Kunst, Codekommentare in JavaScript hinzuzufügen

Mar 18 2023
Das Debuggen von Code ohne Kommentare ist wie ein Detektiv, der versucht, ein Verbrechen aufzuklären, aber der einzige Zeuge spricht eine Fremdsprache, und einige einflussreiche Leute (Kunden) sitzen Ihnen im Nacken, um den Fall gestern aufzuklären. Bildquelle — Zwei Männer schauen auf einen Laptop · Kostenloses Stock-Foto (pexels.

Das Debuggen von Code ohne Kommentare ist wie ein Detektiv, der versucht, ein Verbrechen aufzuklären, aber der einzige Zeuge spricht eine Fremdsprache, und einige einflussreiche Leute (Kunden) sitzen Ihnen im Nacken, um den Fall gestern aufzuklären.

Bildquelle — Zwei Männer schauen auf einen Laptop · Kostenloses Stock-Foto (pexels.com)

Einführung

Das Hinzufügen von Codekommentaren ist aus mehreren Gründen wichtig:

1. Lesbarkeit des Codes. Kommentare helfen Entwicklern, den Zweck des Codes zu verstehen, indem sie ihn in einfacher Sprache erklären und so das Lesen und Ändern erleichtern.

2. Wartung. Kommentare helfen Ihnen, sich an den Zweck Ihres Codes zu erinnern, und erleichtern so die zukünftige Wartung und Aktualisierung, ohne versehentlich Probleme zu verursachen.

3. Zusammenarbeit. Kommentare verhindern Fehler und Fehlinterpretationen in Gemeinschaftsprojekten, indem sie den Kontext für Codeänderungen bereitstellen und sicherstellen, dass alle auf dem gleichen Stand sind.

4. Dokumentation. Codekommentare sorgen für Klarheit über Zweck und Funktionalität und stellen die Einhaltung von Industriestandards und Best Practices sicher. Sie machen den Code lesbarer, wartbarer und einfacher zu handhaben, genau wie die LEGO-Anleitung zum Bau eines Sets.

Indem Entwickler sich die Zeit nehmen, dem Code aussagekräftige und genaue Kommentare hinzuzufügen, können sie die Gesamtqualität und Wartbarkeit ihrer Projekte verbessern.

Mentales Modell zum Hinzufügen von Kommentaren zum Code

Bevor Sie Kommentare zu einem Code schreiben, ist es wichtig, sich über die Absicht im Klaren zu sein. Ein Codekommentar ist ein Kommunikationsmechanismus, der die folgenden Aspekte des Codes kommunizieren soll:

1. Zweck. Erklären Sie den Zweck des Codes, welches Problem er löst und warum er existiert.

2. Eingabe. Geben Sie die für den Code erforderlichen Eingaben an, einschließlich ihrer Typen, erwarteten Werte und ob sie optional oder obligatorisch sind.

3. Ausgabe. Geben Sie die vom Code erzeugte Ausgabe an, einschließlich seines Typs und aller relevanten Details.

4. Nutzung. Geben Sie Beispiele für die Verwendung des Codes an, einschließlich relevanter Codeausschnitte.

5. Verlinkung. Fügen Sie Links zu verwandtem Code und externen Ressourcen hinzu.

6. Best Practices. Heben Sie alle Best Practices oder Fallstricke im Zusammenhang mit dem Code hervor.

Durch die Befolgung des richtigen mentalen Modells können Entwickler sicherstellen, dass ihr Code gut dokumentiert, leicht verständlich und im Laufe der Zeit wartbar ist.

Konsistenz und Standardisierung

Es ist von entscheidender Bedeutung, die Konsistenz der Formatierung und des Kommentarstils in der gesamten Codebasis aufrechtzuerhalten. Eine gute Option ist die Verwendung eines Tools wie JS Doc (JavaScript-Dokumentation), bei dem es sich um eine Reihe von Dokumentationsstandards und Tools zur Dokumentation von JavaScript-Code handelt.

Im Folgenden sind einige der Funktionen von JS Doc aufgeführt:

  • Mithilfe von Tags werden Informationen über die Struktur, Funktion, Parameter, Rückgabewerte und andere Details des Codes erfasst, um eine für Menschen lesbare Referenz für andere Entwickler zu erstellen.
  • Ein JS-Doc-Tag beginnt mit einem @Symbol, gefolgt vom Tag-Namen und allen relevanten Informationen. Beispielsweise paramwird das Tag zur Beschreibung eines Funktionsparameters verwendet, während das returnsTag zur Beschreibung des Rückgabewerts einer Funktion verwendet wird.
  • Es kann Dokumentation in HTML oder anderen Formaten aus Inline-Kommentaren im Quellcode generieren.

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);
  }
}

Häufig verwendete JS-Doc-Tags

Werfen wir einen Blick auf einige JS-Doc-Tags, die recht häufig verwendet werden.

  • @todo

/**
 * @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);
  }
}

Der erste Schritt besteht darin, zu beschreiben, was der Code tut. @descriptionDas Tag wird verwendet, um eine detaillierte Beschreibung einer Funktion, eines Objekts oder einer Eigenschaft bereitzustellen. Sehen wir uns an, wie der Code nach dem Hinzufügen dieses Tags aussieht.

/**
 * @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);
  }
}

Hier kommt der Teil, in dem erklärt wird, welche Eingaben der Code annimmt. @paramTag wird verwendet, um die Parameter einer Funktion zu beschreiben. Wenn man es zum Beispiel hinzufügt, sieht der Code so aus:

/**
 * @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);
  }
}

Bisher wissen wir, was die Funktion tut und welche Eingaben sie benötigt. Als nächstes folgt die Beschreibung des Rückgabewerts einer Funktion. Wenn man das Beispiel ergänzt, @returnssieht der Code so aus:

/**
 * @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);
  }
}

Es kann vorkommen, dass die Funktion auf Fehler stößt und nicht die gewünschten Ergebnisse liefern kann. @throwshilft bei der Dokumentation dieser Szenarien. Wenn man das Beispiel ergänzt, @throwssieht der Code so aus:

/**
 * @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);
  }
}

Manchmal ist der Konsument oder Aufrufer des Codes nicht an den Implementierungsdetails auf niedriger Ebene interessiert und macht sich Sorgen um den Aufruf und das Erhalten der erwarteten Ergebnisse. @exampleDas Tag wird verwendet, um ein Beispiel für die Verwendung einer Funktion oder eines Objekts bereitzustellen. Wenn man das Beispiel ergänzt, @examplesieht der Code so aus:

/**
 * @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);
  }
}

Das @typeJSDoc-Tag wird verwendet, um den Typ einer Variablen, Funktion oder eines Ausdrucks anzugeben. Es liefert zusätzliche Informationen über den Typ der dokumentierten Entität und hilft bei der Lesbarkeit und Wartbarkeit des Codes.

Das @typedefJSDoc-Tag hingegen wird verwendet, um einen benutzerdefinierten Typ zu definieren. Dies kann in komplexen Projekten nützlich sein, in denen Sie benutzerdefinierte Datenstrukturen oder -typen haben, die dokumentiert werden müssen. Der mit definierte benutzerdefinierte Typ kann dann mit dem Tag in anderen Teilen des Codes @typedefreferenziert werden .@type

Im Wesentlichen @typedefwird es verwendet, um einen benutzerdefinierten Typ zu definieren, während @typees verwendet wird, um den Typ einer Entität anzugeben.

Das @propertyJSDoc-Tag wird verwendet, um Eigenschaften eines Objekts oder einer Klasse zu dokumentieren. Es wird normalerweise in Verbindung mit verwendet, @typedefum die Struktur eines Objekts oder einer Klasse zu dokumentieren. Das @propertyTag gibt den Namen und Typ einer Eigenschaft sowie eine Beschreibung ihres Zwecks an.

Durch das Hinzufügen von @type, @typedefund @propertyzum Beispiel sieht der Code folgendermaßen aus:

/**
 * @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);
  }
}

  • @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);
  }
}

Der FactorialCallbackTyp wird dann im @paramTag für den Callback-Parameter der factorialWithCallbackFunktion referenziert.

Beispiel/Demo

Eine Vorschau des obigen Snippets und der Dokumentation, die es mit JS Doc generiert, kann hier eingesehen werden .

Bitte beachten Sie, dass es sich bei dem Beispiel um eine sehr vereinfachte Implementierung handelt, um dem Leser das Potenzial zu zeigen, das erschlossen werden kann, wenn alle diese Techniken verstanden und in seinen Projekten implementiert sind.

Zusammenfassung

In einer Welt voller komplexer und komplizierter Codebasen sind Codekommentare ein Freund, den wir alle brauchen. Mit Vorteilen, die von verbesserter Code-Lesbarkeit bis hin zu automatisch generierter Dokumentation reichen, sind Tools wie JS Doc für jedes Entwicklungs-Toolkit unverzichtbar.

Unabhängig davon, ob Sie ein erfahrener Entwickler sind oder gerade erst anfangen: Wenn Sie sich die Zeit nehmen, Ihren Code auf standardisierte Weise zu dokumentieren, wird das Produkt nicht nur kurzfristig davon profitieren, sondern auch die Langlebigkeit und Wartbarkeit der Codebasis über Jahre hinweg sicherstellen.

Ich würde Sie ermutigen, diese Systeme in Ihren Projekten zu verwenden. Vertrauen Sie mir, Ihr zukünftiges Ich wird es Ihnen danken!