2022 年 5 月 22 日

评论

正如我们在 代码结构 一章中所了解的,注释可以是单行的:以 // 开头,也可以是多行的:/* ... */

我们通常使用它们来描述代码如何工作以及为什么工作。

乍一看,注释可能很明显,但编程新手经常错误地使用它们。

糟糕的注释

新手倾向于使用注释来解释“代码中发生了什么”。就像这样

// This code will do this thing (...) and that thing (...)
// ...and who knows what else...
very;
complex;
code;

但在好的代码中,这种“解释性”注释的数量应该最少。说真的,没有它们,代码也应该很容易理解。

对此有一个很好的规则:“如果代码如此不清楚以至于需要注释,那么也许应该重写它”。

秘诀:提取函数

有时,用函数替换一段代码是有益的,就像这里

function showPrimes(n) {
  nextPrime:
  for (let i = 2; i < n; i++) {

    // check if i is a prime number
    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert(i);
  }
}

更好的变体,使用提取的函数 isPrime

function showPrimes(n) {

  for (let i = 2; i < n; i++) {
    if (!isPrime(i)) continue;

    alert(i);
  }
}

function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if (n % i == 0) return false;
  }

  return true;
}

现在我们可以轻松理解代码了。函数本身就变成了注释。这样的代码被称为自描述的

秘诀:创建函数

如果我们有一个像这样的长“代码表”

// here we add whiskey
for(let i = 0; i < 10; i++) {
  let drop = getWhiskey();
  smell(drop);
  add(drop, glass);
}

// here we add juice
for(let t = 0; t < 3; t++) {
  let tomato = getTomato();
  examine(tomato);
  let juice = press(tomato);
  add(juice, glass);
}

// ...

那么将其重构为函数可能是一个更好的变体,如下所示

addWhiskey(glass);
addJuice(glass);

function addWhiskey(container) {
  for(let i = 0; i < 10; i++) {
    let drop = getWhiskey();
    //...
  }
}

function addJuice(container) {
  for(let t = 0; t < 3; t++) {
    let tomato = getTomato();
    //...
  }
}

同样,函数本身会告诉我们发生了什么。没有什么可注释的。而且,拆分后代码结构也更好。每个函数做什么、需要什么以及返回什么都很清楚。

实际上,我们无法完全避免“解释性”注释。存在复杂的算法。而且存在用于优化目的的智能“调整”。但通常我们应尝试保持代码简单且自描述。

好的注释

因此,解释性注释通常很糟糕。哪些注释是好的?

描述架构
提供组件的高级概述,它们如何交互,在各种情况下控制流是什么……简而言之,就是代码的鸟瞰图。有一种特殊语言 UML 用于构建解释代码的高级架构图。绝对值得学习。
记录函数参数和用法
有一种特殊语法 JSDoc 用于记录函数:用法、参数、返回值。

例如

/**
 * Returns x raised to the n-th power.
 *
 * @param {number} x The number to raise.
 * @param {number} n The power, must be a natural number.
 * @return {number} x raised to the n-th power.
 */
function pow(x, n) {
  ...
}

此类注释使我们能够理解函数的目的,并以正确的方式使用它,而无需查看其代码。

顺便说一下,许多编辑器(如 WebStorm)也可以理解它们,并使用它们来提供自动完成和一些自动代码检查。

此外,还有 JSDoc 3 等工具,可以从注释生成 HTML 文档。您可以在 https://jsdoc.node.org.cn 上阅读有关 JSDoc 的更多信息。

为什么以这种方式解决任务?

写下的内容很重要。但未写下的内容可能更重要,以便理解正在发生的事情。为什么以这种方式解决任务?代码没有给出答案。

如果有多种方法可以解决任务,为什么选择这种方法?特别是当它不是最明显的方法时。

如果没有此类注释,则可能出现以下情况

  1. 您(或您的同事)打开一段时间前编写的代码,并发现它“次优”。
  2. 您认为:“我当时多么愚蠢,我现在多么聪明”,并使用“更明显、更正确”的变体进行重写。
  3. ……重写的冲动是好的。但在过程中,您会发现“更明显”的解决方案实际上是缺乏的。您甚至隐约记得原因,因为您很久以前就尝试过。您恢复到正确的变体,但时间浪费了。

解释解决方案的注释非常重要。它们有助于以正确的方式继续开发。

代码的任何微妙特征?它们在哪里使用?

如果代码有任何微妙且违反直觉的内容,那么绝对值得评论。

总结

优秀开发人员的一个重要标志是注释:它们的存在甚至不存在。

好的注释使我们能够很好地维护代码,在延迟后返回代码并更有效地使用它。

对此进行注释

  • 总体架构,高级视图。
  • 函数用法。
  • 重要的解决方案,尤其是在不明显的情况下。

避免注释

  • 讲述“代码如何工作”和“代码做什么”。
  • 仅在不可能使代码如此简单且自描述以至于不需要注释时才添加注释。

注释还用于自动文档化工具,如 JSDoc3:它们会读取注释并生成 HTML 文档(或其他格式的文档)。

教程地图

评论

在注释之前阅读此内容…
  • 如果您有改进建议,请 提交 GitHub 问题或提交拉取请求,而不是进行注释。
  • 如果您无法理解文章中的某些内容,请详细说明。
  • 要插入几行代码,请使用 <code> 标记,对于多行代码,请将其包装在 <pre> 标记中,对于超过 10 行的代码,请使用沙盒 (plnkrjsbincodepen…)。