JavaScript 程式碼示例編寫指南

以下指南涵蓋了為 MDN Web Docs 編寫 JavaScript 示例程式碼。本文列出了編寫簡潔易懂的示例的規則,以便儘可能多的人理解。

JavaScript 程式碼示例的一般指南

本節解釋了編寫 JavaScript 程式碼示例時需要牢記的一般指南。後面的部分將介紹更具體的細節。

選擇格式

關於正確縮排、空格和行長的意見一直存在爭議。關於這些主題的討論分散了建立和維護內容的注意力。

在 MDN Web Docs 上,我們使用 Prettier 作為程式碼格式化程式來保持程式碼風格的一致性(並避免無關的討論)。您可以查閱我們的 配置檔案 以瞭解當前規則,並閱讀 Prettier 文件

Prettier 格式化所有程式碼並保持樣式一致。但是,您還需要遵循一些其他規則。

使用現代 JavaScript 功能

一旦每個主要的瀏覽器(Chrome、Edge、Firefox 和 Safari)都支援新功能,您就可以使用它們。

陣列

陣列建立

對於建立陣列,請使用字面量而不是建構函式。

像這樣建立陣列

js
const visitedCities = [];

建立陣列時不要這樣做

js
const visitedCities = new Array(length);

專案新增

向陣列新增專案時,請使用 push() 而不是直接賦值。考慮以下陣列

js
const pets = [];

像這樣向陣列新增專案

js
pets.push("cat");

不要像這樣向陣列新增專案

js
pets[pets.length] = "cat";

非同步方法

編寫非同步程式碼可以提高效能,並且應在可能的情況下使用。特別是,您可以使用

當這兩種技術都可行時,我們更喜歡使用更簡單的 async/await 語法。不幸的是,除非您位於 ECMAScript 模組中,否則您無法在頂層使用 await。Node.js 使用的 CommonJS 模組不是 ES 模組。如果您的示例旨在在任何地方使用,請避免頂層 await

註釋

註釋對於編寫良好的程式碼示例至關重要。它們闡明瞭程式碼的意圖並幫助開發人員理解它。請特別注意它們。

  • 如果程式碼的目的或邏輯不明確,請新增一條帶有您的意圖的註釋,如下所示
    js
    let total = 0;
    
    // Calculate the sum of the four first elements of arr
    for (let i = 0; i < 4; i++) {
      total += arr[i];
    }
    
    另一方面,用散文重述程式碼不是註釋的良好用途
    js
    let total = 0;
    
    // For loop from 1 to 4
    for (let i = 0; i < 4; i++) {
      // Add value to the total
      total += arr[i];
    }
    
  • 當函式具有描述其作用的顯式名稱時,也不需要註釋。編寫
    js
    closeConnection();
    
    不要編寫
    js
    closeConnection(); // Closing the connection
    

使用單行註釋

單行註釋以 // 標記,而不是用 /* … */ 括起來的塊註釋。

通常,使用單行註釋來註釋程式碼。編寫者必須在註釋的每一行都使用 // 標記,以便更容易在視覺上注意到已註釋掉的程式碼。此外,此約定允許在除錯時使用 /* … */ 註釋掉程式碼部分。

  • 在斜槓和註釋之間留一個空格。以大寫字母開頭,像句子一樣,但不要在註釋末尾加句號。
    js
    // This is a well-written single-line comment
    
  • 如果註釋不是緊跟在新的縮排級別之後,請新增一個空行,然後添加註釋。它將建立一個程式碼塊,使註釋所指的內容一目瞭然。此外,將您的註釋放在單獨的行上,放在它們所指的程式碼之前。這在以下示例中顯示
    js
    function checkout(goodsPrice, shipmentPrice, taxes) {
      // Calculate the total price
      const total = goodsPrice + shipmentPrice + taxes;
    
      // Create and append a new paragraph to the document
      const para = document.createElement("p");
      para.textContent = `Total price is ${total}`;
      document.body.appendChild(para);
    }
    

日誌輸出

  • 在旨在在生產環境中執行的程式碼中,您很少需要在記錄某些資料時添加註釋。在程式碼示例中,我們經常使用 console.log()console.error() 或類似函式來輸出重要值。為了幫助讀者在不執行程式碼的情況下理解會發生什麼,您可以在函式之後加上將產生的日誌的註釋。編寫
    js
    function exampleFunc(fruitBasket) {
      console.log(fruitBasket); // ['banana', 'mango', 'orange']
    }
    
    不要編寫
    js
    function exampleFunc(fruitBasket) {
      // Logs: ['banana', 'mango', 'orange']
      console.log(fruitBasket);
    }
    
  • 如果行變得太長,請在函式之後添加註釋,如下所示
    js
    function exampleFunc(fruitBasket) {
      console.log(fruitBasket);
      // ['banana', 'mango', 'orange', 'apple', 'pear', 'durian', 'lemon']
    }
    

多行註釋

簡短的註釋通常更好,因此請嘗試將它們保持在一行 60-80 個字元內。如果這不可行,請在每一行的開頭使用 //

js
// This is an example of a multi-line comment.
// The imaginary function that follows has some unusual
// limitations that I want to call out.
// Limitation 1
// Limitation 2

不要使用 /* … */

js
/* This is an example of a multi-line comment.
  The imaginary function that follows has some unusual
  limitations that I want to call out.
  Limitation 1
  Limitation 2 */

使用註釋標記省略號

使用省略號(…)跳過冗餘程式碼對於保持示例簡潔是必要的。但是,編寫者應該謹慎地進行操作,因為開發人員經常將示例複製貼上到他們的程式碼中,並且我們所有的程式碼示例都應該是有效的 JavaScript。

在 JavaScript 中,您應該將省略號()放在註釋中。如果可能,請說明重用此程式碼段的人員應新增什麼操作。

使用註釋表示省略號(…)更明確,可以防止開發人員複製貼上程式碼示例時出錯。編寫

js
function exampleFunc() {
  // Add your code here
  // …
}

不要像這樣使用省略號(…)

js
function exampleFunc() {}

註釋掉引數

在編寫程式碼時,您通常會省略不需要的引數。但在某些程式碼示例中,您希望演示您沒有使用某些可能的引數。

為此,請在引數列表中使用 /* … */。這是僅使用單行註釋 (//) 規則的例外情況。

js
array.forEach((value /* , index, array */) => {
  // …
});

函式

函式名稱

對於函式名稱,請使用 駝峰式命名法,以小寫字母開頭。在適當的情況下使用簡潔、易於閱讀和語義化的名稱。

以下是一個正確的函式名稱示例

js
function sayHello() {
  console.log("Hello!");
}

不要使用以下函式名稱

js
function SayHello() {
  console.log("Hello!");
}

function doIt() {
  console.log("Hello!");
}

函式宣告

  • 如果可能,請使用函式宣告而不是函式表示式來定義函式。以下是推薦的宣告函式的方式
    js
    function sum(a, b) {
      return a + b;
    }
    
    這不是定義函式的好方法
    js
    let sum = function (a, b) {
      return a + b;
    };
    
  • 當使用匿名函式作為回撥(傳遞給另一個方法呼叫的函式)時,如果您不需要訪問 this,請使用箭頭函式使程式碼更短更簡潔。以下是推薦的方式
    js
    const array1 = [1, 2, 3, 4];
    const sum = array1.reduce((a, b) => a + b);
    
    而不是這樣
    js
    const array1 = [1, 2, 3, 4];
    const sum = array1.reduce(function (a, b) {
      return a + b;
    });
    
  • 考慮避免使用箭頭函式將函式分配給識別符號。特別是,不要將箭頭函式用於方法。使用帶有 function 關鍵字的函式宣告
    js
    function x() {
      // …
    }
    
    不要這樣做
    js
    const x = () => {
      // …
    };
    
  • 當使用箭頭函式時,如果可能,請使用 隱式返回(也稱為表示式體
    js
    arr.map((e) => e.id);
    
    而不是
    js
    arr.map((e) => {
      return e.id;
    });
    

迴圈和條件語句

迴圈初始化

當需要 迴圈 時,請從 for(;;)for...ofwhile 等中選擇合適的迴圈。

  • 當遍歷所有集合元素時,避免使用經典的 for (;;) 迴圈;優先使用 for...offorEach()。請注意,如果您使用的是不是 Array 的集合,則必須檢查 for...of 是否實際受支援(它要求變數是可迭代的),或者 forEach() 方法是否實際存在。使用 for...of
    js
    const dogs = ["Rex", "Lassie"];
    for (const dog of dogs) {
      console.log(dog);
    }
    
    forEach()
    js
    const dogs = ["Rex", "Lassie"];
    dogs.forEach((dog) => {
      console.log(dog);
    });
    
    不要使用 for (;;)——您不僅必須新增額外的索引 i,而且還必須跟蹤陣列的長度。這對於初學者來說很容易出錯。
    js
    const dogs = ["Rex", "Lassie"];
    for (let i = 0; i < dogs.length; i++) {
      console.log(dogs[i]);
    }
    
  • 確保透過對 for...of 使用 const 關鍵字或對其他迴圈使用 let 關鍵字來正確定義初始化程式。不要省略它。這些是正確的示例
    js
    const cats = ["Athena", "Luna"];
    for (const cat of cats) {
      console.log(cat);
    }
    
    for (let i = 0; i < 4; i++) {
      result += arr[i];
    }
    
    下面的示例不遵循初始化的推薦指南(它隱式建立了一個全域性變數,並且在嚴格模式下會失敗)
    js
    const cats = ["Athena", "Luna"];
    for (i of cats) {
      console.log(i);
    }
    
  • 當您需要同時訪問值和索引時,可以使用 .forEach() 代替 for (;;)。編寫
    js
    const gerbils = ["Zoé", "Chloé"];
    gerbils.forEach((gerbil, i) => {
      console.log(`Gerbil #${i}: ${gerbil}`);
    });
    
    不要編寫
    js
    const gerbils = ["Zoé", "Chloé"];
    for (let i = 0; i < gerbils.length; i++) {
      console.log(`Gerbil #${i}: ${gerbils[i]}`);
    }
    

警告:永遠不要對陣列和字串使用 for...in

注意: 儘可能避免使用 for 迴圈。如果正在使用 Array(或在某些操作中使用 String),請考慮使用更具語義的迭代方法,例如 map()every()findIndex()find()includes() 等等。

控制語句

對於 if...else 控制語句,需要注意一個要點。如果 if 語句以 return 結束,則不要新增 else 語句。

if 語句之後直接繼續執行。請編寫如下程式碼:

js
if (test) {
  // Perform something if test is true
  // …
  return;
}

// Perform something if test is false
// …

不要編寫

js
if (test) {
  // Perform something if test is true
  // …
  return;
} else {
  // Perform something if test is false
  // …
}

在控制流語句和迴圈中使用花括號

雖然 ifforwhile 等控制流語句在內容僅包含一條語句時不需要使用花括號,但您始終應該使用花括號。請編寫如下程式碼:

js
for (const car of storedCars) {
  car.paint("red");
}

不要編寫

js
for (const car of storedCars) car.paint("red");

這可以防止在新增更多語句時忘記新增花括號。

switch 語句

switch 語句可能有點棘手。

  • 在特定情況下,不要在 return 語句後新增 break 語句。請改為像這樣編寫 return 語句:
    js
    switch (species) {
      case "chicken":
        return farm.shed;
      case "horse":
        return corral.entry;
      default:
        return "";
    }
    
    如果您添加了 break 語句,它將無法訪問。請勿編寫如下程式碼:
    js
    switch (species) {
      case "chicken":
        return farm.shed;
        break;
      case "horse":
        return corral.entry;
        break;
      default:
        return "";
    }
    
  • 使用 default 作為最後一個 case,並且不要以 break 語句結束它。如果您需要以其他方式處理,請添加註釋解釋原因。
  • 請記住,當您為某個 case 宣告區域性變數時,需要使用花括號來定義作用域。
    js
    switch (fruits) {
      case "Orange": {
        const slice = fruit.slice();
        eat(slice);
        break;
      }
      case "Apple": {
        const core = fruit.extractCore();
        recycle(core);
        break;
      }
    }
    

錯誤處理

  • 如果程式的某些狀態丟擲未捕獲的錯誤,它們將停止執行並可能降低示例的實用性。因此,您應該使用 try...catch 塊捕獲錯誤,如下所示:
    js
    try {
      console.log(getResult());
    } catch (e) {
      console.error(e);
    }
    
  • 當您不需要 catch 語句的引數時,請省略它。
    js
    try {
      console.log(getResult());
    } catch {
      console.error("An error happened!");
    }
    

注意: 請記住,只有可恢復的錯誤才應該被捕獲和處理。所有不可恢復的錯誤都應該被放行並冒泡到呼叫棧。

物件

物件名稱

  • 在定義類時,請使用PascalCase(以大寫字母開頭)作為類名,並使用camelCase(以小寫字母開頭)作為物件屬性和方法名。
  • 在定義物件例項時,無論是字面量還是透過建構函式,請使用camelCase,以小寫字元開頭,作為例項名稱。例如:
    js
    const hanSolo = new Person("Han Solo", 25, "he/him");
    
    const luke = {
      name: "Luke Skywalker",
      age: 25,
      pronouns: "he/him",
    };
    

物件建立

對於建立普通物件(即,不涉及類時),請使用字面量而不是建構函式。

例如,請執行以下操作:

js
const object = {};

不要像這樣建立普通物件:

js
const object = new Object();

物件類

  • 對於物件,請使用 ES 類語法,而不是舊式建構函式。例如,這是推薦的方式:
    js
    class Person {
      constructor(name, age, pronouns) {
        this.name = name;
        this.age = age;
        this.pronouns = pronouns;
      }
    
      greeting() {
        console.log(`Hi! I'm ${this.name}`);
      }
    }
    
  • 使用 extends 進行繼承
    js
    class Teacher extends Person {
      // …
    }
    

方法

要定義方法,請使用方法定義語法。

js
const obj = {
  foo() {
    // …
  },
  bar() {
    // …
  },
};

而不是:

js
const obj = {
  foo: function () {
    // …
  },
  bar: function () {
    // …
  },
};

物件屬性

  • Object.prototype.hasOwnProperty() 方法已被棄用,取而代之的是 Object.hasOwn()
  • 在可能的情況下,使用簡寫方式避免屬性識別符號的重複。請編寫如下程式碼:
    js
    function createObject(name, age) {
      return { name, age };
    }
    
    不要編寫
    js
    function createObject(name, age) {
      return { name: name, age: age };
    }
    

運算子

本節列出了我們關於何時使用哪些運算子的建議。

條件運算子

當您希望根據條件將字面量值儲存到變數中時,請使用 條件(三元)運算子 而不是 if...else 語句。此規則也適用於返回值。請編寫如下程式碼:

js
const x = condition ? 1 : 2;

不要編寫

js
let x;
if (condition) {
  x = 1;
} else {
  x = 2;
}

條件運算子在建立字串以記錄資訊時很有用。在這種情況下,使用常規的 if...else 語句會導致用於記錄等副作用操作的程式碼塊過長,從而模糊了示例的中心點。

嚴格相等運算子

優先使用 嚴格相等(三個等號)和不等運算子,而不是鬆散相等(兩個等號)和不等運算子。

請像這樣使用嚴格相等和不等運算子:

js
name === "Shilpa";
age !== 25;

請勿使用鬆散相等和不等運算子,如下所示:

js
name == "Shilpa";
age != 25;

如果您需要使用 ==!=,請記住 == null 是唯一可接受的情況。由於 TypeScript 將在所有其他情況下失敗,因此我們不希望在示例程式碼中包含它們。請考慮添加註釋以解釋您為何需要它。

布林測試的快捷方式

優先使用布林測試的快捷方式。例如,使用 if (x)if (!x),而不是 if (x === true)if (x === false),除非不同型別的真值或假值以不同的方式處理。

字串

字串文字可以包含在單引號中,如 'A string',也可以包含在雙引號中,如 "A string"。無需擔心使用哪一個;Prettier 會保持一致。

模板文字

要將值插入字串,請使用 模板文字

  • 這是一個使用模板文字的推薦方式的示例。它們的使用可以避免很多空格錯誤。
    js
    const name = "Shilpa";
    console.log(`Hi! I'm ${name}!`);
    
    不要像這樣連線字串:
    js
    const name = "Shilpa";
    console.log("Hi! I'm" + name + "!"); // Hi! I'mShilpa!
    
  • 不要過度使用模板文字。如果沒有替換,請改用普通字串文字。

變數

變數名稱

良好的變數名稱對於理解程式碼至關重要。

  • 使用簡短的識別符號,並避免不常見的縮寫。良好的變數名稱通常在 3 到 10 個字元之間,但這只是一個提示。例如,accelerometer 比為了字元長度而縮寫為 acclmtr 更具描述性。
  • 嘗試使用與現實世界相關的示例,其中每個變數都具有清晰的語義。僅當示例簡單且人為構造時,才回退到佔位符名稱,例如 foobar
  • 不要使用 匈牙利命名法。不要在變數名前新增其型別。例如,編寫 bought = car.buyer !== null 而不是 bBought = oCar.sBuyer != null,或 name = "John Doe" 而不是 sName = "John Doe"
  • 對於集合,避免在名稱中新增型別,例如 list、array、queue。使用複數形式的內容名稱。例如,對於汽車陣列,使用 cars 而不是 carArraycarList。可能存在例外情況,例如當您希望在沒有特定應用程式上下文的情況下顯示功能的抽象形式時。
  • 對於原始值,請使用camelCase,以小寫字元開頭。不要使用 _。在適當的情況下使用簡潔、易於理解和語義化的名稱。例如,使用 currencyName 而不是 currency_name
  • 避免使用冠詞和所有格。例如,使用 car 而不是 myCaraCar。可能存在例外情況,例如當在沒有實際上下文的情況下描述一般功能時。
  • 請像這樣使用變數名稱:
    js
    const playerScore = 0;
    const speed = distance / time;
    
    不要像這樣命名變數:
    js
    const thisIsaveryLONGVariableThatRecordsPlayerscore345654 = 0;
    const s = d / t;
    

注意: 唯一允許不使用易於理解和語義化的名稱的地方是在存在非常普遍認可的約定的地方,例如對於迴圈迭代器使用 ij

變數宣告

在宣告變數和常量時,請使用 letconst 關鍵字,而不是 var。以下示例展示了 MDN Web Docs 上推薦的和不推薦的內容:

  • 如果變數不會被重新賦值,請優先使用 const,如下所示:
    js
    const name = "Shilpa";
    console.log(name);
    
  • 如果要更改變數的值,請使用 let,如下所示:
    js
    let age = 40;
    age++;
    console.log("Happy birthday!");
    
  • 以下示例在應該使用 const 的地方使用了 let。程式碼可以工作,但我們希望避免在 MDN Web Docs 程式碼示例中使用這種用法。
    js
    let name = "Shilpa";
    console.log(name);
    
  • 以下示例對被重新賦值的變數使用了 const。重新賦值將丟擲錯誤。
    js
    const age = 40;
    age++;
    console.log("Happy birthday!");
    
  • 以下示例使用了 var,汙染了全域性作用域。
    js
    var age = 40;
    var name = "Shilpa";
    
  • 每行宣告一個變數,如下所示:
    js
    let var1;
    let var2;
    let var3 = "Apapou";
    let var4 = var3;
    
    不要在一行中宣告多個變數,用逗號分隔或使用鏈式宣告。請避免像這樣宣告變數:
    js
    let var1, var2;
    let var3 = var4 = "Apapou"; // var4 is implicitly created as a global variable; fails in strict mode
    

型別強制轉換

避免隱式型別強制轉換。特別是,避免使用 +val 將值強制轉換為數字,以及 "" + val 將值強制轉換為字串。請改用 Number()String()(不帶 new)。請編寫如下程式碼:

js
class Person {
  #name;
  #birthYear;

  constructor(name, year) {
    this.#name = String(name);
    this.#birthYear = Number(year);
  }
}

不要編寫

js
class Person {
  #name;
  #birthYear;

  constructor(name, year) {
    this.#name = "" + name;
    this.#birthYear = +year;
  }
}

避免使用的 Web API

除了這些 JavaScript 語言特性之外,我們還建議您注意一些與 Web API 相關的準則。

避免瀏覽器字首

如果所有主要瀏覽器(Chrome、Edge、Firefox 和 Safari)都支援某個功能,請不要為該功能新增字首。請編寫如下程式碼:

js
const context = new AudioContext();

避免新增字首帶來的複雜性。請勿編寫如下程式碼:

js
const AudioContext = window.AudioContext || window.webkitAudioContext;
const context = new AudioContext();

相同的規則也適用於 CSS 字首。

避免已棄用的 API

當某個方法、屬性或整個介面已棄用時,請不要使用它(在其文件之外)。請改用現代 API。

這是一個不完整的 Web API 列表,其中列出了應避免的 API 以及應使用哪些 API 來替換它們:

  • 使用 fetch() 而不是 XHR(XMLHttpRequest)。
  • 在 Web Audio API 中,使用 AudioWorklet 而不是 ScriptProcessorNode

使用安全可靠的 API

  • 不要使用 Element.innerHTML 將純文字內容插入元素中;請改用 Node.textContent。如果開發人員不控制引數,則屬性 innerHTML 會導致安全問題。我們作為編寫者越少使用它,開發人員複製貼上我們的程式碼時產生的安全漏洞就越少。以下示例演示了 textContent 的用法。
    js
    const text = "Hello to all you good people";
    const para = document.createElement("p");
    para.textContent = text;
    
    不要使用 innerHTML純文字插入 DOM 節點中。
    js
    const text = "Hello to all you good people";
    const para = document.createElement("p");
    para.innerHTML = text;
    
  • alert() 函式不可靠。它在 MDN Web Docs 上位於 <iframe> 內的即時示例中不起作用。此外,它會對整個視窗進行模態顯示,這很煩人。在靜態程式碼示例中,請使用 console.log()console.error()。在 即時示例 中,請避免使用 console.log()console.error(),因為它們不會顯示。請使用專用的 UI 元素。

使用合適的日誌方法

  • 在記錄訊息時,請使用 console.log()
  • 在記錄錯誤時,請使用 console.error()

另請參閱

JavaScript 語言參考 - 瀏覽我們的 JavaScript 參考頁面,檢視一些良好、簡潔、有意義的 JavaScript 程式碼片段。