Javascript 注釋

2023-02-17 10:38 更新

正如我們在 代碼結(jié)構(gòu) 一章所了解到的那樣,注釋可以是以 // 開始的單行注釋,也可以是 /* ... */ 結(jié)構(gòu)的多行注釋。

我們通常通過注釋來描述代碼怎樣工作和為什么這樣工作。

乍一看,寫注釋可能很簡單,但初學(xué)者在編程的時候,經(jīng)常錯誤地使用注釋。

糟糕的注釋

新手傾向于使用注釋來解釋“代碼中發(fā)生了什么”。就像這樣:

// 這里的代碼會先做這件事(……)然后做那件事(……)
// ……誰知道還有什么……
very;
complex;
code;

但在好的代碼中,這種“解釋性”注釋的數(shù)量應(yīng)該是最少的。嚴(yán)格地說,就算沒有它們,代碼也應(yīng)該很容易理解。

關(guān)于這一點有一個很棒的原則:“如果代碼不夠清晰以至于需要一個注釋,那么或許它應(yīng)該被重寫?!?/p>

配方:分解函數(shù)

有時候,用一個函數(shù)來代替一個代碼片段是更好的,就像這樣:

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

    // 檢測 i 是否是一個質(zhì)數(shù)(素數(shù))
    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert(i);
  }
}

更好的變體,使用一個分解出來的函數(shù) 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;
}

現(xiàn)在我們可以很容易地理解代碼了。函數(shù)自己就變成了一個注釋。這種代碼被稱為 自描述型 代碼。

配方:創(chuàng)建函數(shù)

如果我們有一個像下面這樣很長的代碼塊:

// 在這里我們添加威士忌(譯注:國外的一種酒)
for(let i = 0; i < 10; i++) {
  let drop = getWhiskey();
  smell(drop);
  add(drop, glass);
}

// 在這里我們添加果汁
for(let t = 0; t < 3; t++) {
  let tomato = getTomato();
  examine(tomato);
  let juice = press(tomato);
  add(juice, glass);
}

// ...

我們像下面這樣,將上面的代碼重構(gòu)為函數(shù),可能會是一個更好的變體:

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();
    //...
  }
}

同樣,函數(shù)本身就可以告訴我們發(fā)生了什么。沒有什么地方需要注釋。并且分割之后代碼的結(jié)構(gòu)也更好了。每一個函數(shù)做什么、需要什么和返回什么都非常地清晰。

實際上,我們不能完全避免“解釋型”注釋。例如在一些復(fù)雜的算法中,會有一些出于優(yōu)化的目的而做的一些巧妙的“調(diào)整”。但是通常情況下,我們應(yīng)該盡可能地保持代碼的簡單和“自我描述”性。

好的注釋

所以,解釋性注釋通常來說都是不好的。那么哪一種注釋才是好的呢?

描述架構(gòu)

對組件進行高層次的整體概括,它們?nèi)绾蜗嗷プ饔?、各種情況下的控制流程是什么樣的……簡而言之 —— 代碼的鳥瞰圖。有一個專門用于構(gòu)建代碼的高層次架構(gòu)圖,以對代碼進行解釋的特殊編程語言 UML。絕對值得學(xué)習(xí)。

記錄函數(shù)的參數(shù)和用法

有一個專門用于記錄函數(shù)的語法 JSDoc:用法、參數(shù)和返回值。

例如:

/**
 * 返回 x 的 n 次冪的值。
 *
 * @param {number} x 要改變的值。
 * @param {number} n 冪數(shù),必須是一個自然數(shù)。
 * @return {number} x 的 n 次冪的值。
 */
function pow(x, n) {
  ...
}

這種注釋可以幫助我們理解函數(shù)的目的,并且不需要研究其內(nèi)部的實現(xiàn)代碼,就可以直接正確地使用它。

順便說一句,很多諸如 WebStorm 這樣的編輯器,都可以很好地理解和使用這些注釋,來提供自動補全和一些自動化代碼檢查工作。

當(dāng)然,也有一些像 JSDoc 3 這樣的工具,可以通過注釋直接生成 HTML 文檔。你可以在 https://jsdoc.app 閱讀更多關(guān)于 JSDoc 的信息。

為什么任務(wù)以這種方式解決?

寫了什么代碼很重要。但是為什么  那樣寫可能對于理解正在發(fā)生什么更重要。為什么任務(wù)是通過這種方式解決的?代碼并沒有給出答案。

如果有很多種方法都可以解決這個問題,為什么偏偏是這一種?尤其當(dāng)它不是最顯而易見的那一種的時候。

沒有這樣的注釋的話,就可能會發(fā)生下面的情況:

  1. 你(或者你的同事)打開了前一段時間寫的代碼,看到它不是最理想的實現(xiàn)方式。
  2. 你會想:“我當(dāng)時是有多蠢啊,現(xiàn)在我真是太聰明了”,然后用“更顯而易見且正確的”方式重寫了一遍。
  3. ……重寫的這股沖動勁是好的。但是在重寫的過程中你發(fā)現(xiàn)“更顯而易見”的解決方案實際上是有缺陷的。你甚至依稀地想起了為什么會這樣,因為你很久之前就已經(jīng)嘗試過這樣做了。于是你又還原了那個正確的實現(xiàn),但是時間已經(jīng)浪費了。

解決方案的注釋非常的重要。它們可以幫助你以正確的方式繼續(xù)開發(fā)。

代碼有哪些巧妙的特性?它們被用在了什么地方?

如果代碼存在任何巧妙和不顯而易見的方法,那絕對需要注釋。

總結(jié)

一個好的開發(fā)者的標(biāo)志之一就是他的注釋:它們的存在甚至它們的缺席(譯注:在該注釋的地方注釋,在不需要注釋的地方則不注釋,甚至寫得好的自描述函數(shù)本身就是一種注釋)。

好的注釋可以使我們更好地維護代碼,一段時間之后依然可以更高效地回到代碼高效開發(fā)。

注釋這些內(nèi)容:

  • 整體架構(gòu),高層次的觀點。
  • 函數(shù)的用法。
  • 重要的解決方案,特別是在不是很明顯時。

避免注釋:

  • 描述“代碼如何工作”和“代碼做了什么”。
  • 避免在代碼已經(jīng)足夠簡單或代碼有很好的自描述性而不需要注釋的情況下,還寫些沒必要的注釋。

注釋也被用于一些如 JSDoc3 等文檔自動生成工具:它們讀取注釋然后生成 HTML 文檔(或者其他格式的文檔)。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號