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.dev.org.tw 閱讀有關 JSDoc 的更多資訊。

為何任務會以這種方式解決?

寫下來的內容很重要。但未寫下來的內容可能更重要,才能了解正在發生的事情。為何任務會以這種方式解決?程式碼沒有提供答案。

如果有多種方法可以解決任務,為何選擇這種方法?特別是當它並非最明顯的方法時。

沒有此類註解時,可能會發生以下情況

  1. 您(或您的同事)開啟一段時間前編寫的程式碼,並發現它「次佳」。
  2. 您想:「我當時有多愚蠢,現在又有多聰明」,並使用「更明顯且正確」的變體進行改寫。
  3. …改寫的衝動很好。但在這個過程中,您會發現「更明顯」的解決方案實際上是有缺陷的。您甚至隱約記得原因,因為您很久以前就已經嘗試過了。您改回正確的變體,但時間已經浪費了。

解釋解決方案的註解非常重要。它們有助於以正確的方式繼續開發。

程式碼的任何微妙特徵?它們在哪裡使用?

如果程式碼有任何微妙且反直覺的地方,絕對值得註解。

摘要

優秀開發人員的重要標誌是註解:它們的存在,甚至它們的缺失。

良好的註解讓我們能夠很好地維護程式碼,在延遲後重新檢視它,並更有效地使用它。

註解此內容

  • 整體架構、高階檢視。
  • 函式使用。
  • 重要的解決方案,特別是不容易立即理解時。

避免註解

  • 說明「程式碼如何運作」和「程式碼的作用」。
  • 僅在無法讓程式碼變得如此簡單且具有自述性,而不需要它們時才將它們放入。

註解也用於自動文件編寫工具,例如 JSDoc3:它們會讀取註解並產生 HTML 文件(或其他格式的文件)。

教學課程地圖

註解

在註解之前閱讀此內容…
  • 如果您有改進建議,請 提交 GitHub 問題 或提交拉取請求,而不是發表評論。
  • 如果您無法理解文章中的某些內容,請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,若要插入多行,請將其包裝在 <pre> 標籤中,若要插入 10 行以上,請使用沙盒 (plnkrjsbincodepen…)