編寫 JavaScript 程式碼示例的指南

以下指南涵蓋了為 MDN Web 文件編寫 JavaScript 示例程式碼。本文列出了編寫簡潔示例的規則,以使盡可能多的人能夠理解。

JavaScript 程式碼示例通用指南

本節解釋了編寫 JavaScript 程式碼示例時要記住的通用指南。後面的章節將涵蓋更具體的細節。

選擇格式

關於正確縮排、空格和行長度的看法一直備受爭議。關於這些話題的討論會分散建立和維護內容的注意力。

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

Prettier 格式化所有程式碼並保持風格一致。然而,還有一些額外的規則需要您遵循。

在支援的情況下使用現代 JavaScript 功能

一旦所有主流瀏覽器——Chrome、Edge、Firefox 和 Safari——都支援新功能(也稱為 Baseline),您就可以使用它們。

此規則不適用於頁面上正在記錄的 JavaScript 功能(它由 收錄標準 決定)。例如,您可以記錄 非標準或實驗性 功能,並編寫完整的示例來演示其行為,但您應避免在其他不相關功能的演示中使用這些功能,例如 Web API。

陣列

陣列建立

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

像這樣建立陣列

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 array = [1, 2, 3, 4];
    const sum = array.reduce((a, b) => a + b);
    

    而不是這樣

    js
    const array = [1, 2, 3, 4];
    const sum = array.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 作為最後一個情況,並且不要以 break 語句結束。如果您需要以不同方式執行此操作,請添加註釋解釋原因。

  • 請記住,當您為案例宣告區域性變數時,您需要使用大括號來定義作用域

    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),除非不同型別的 truthy 或 falsy 值需要區別處理。

字串

字串字面量可以用單引號括起來,如 '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 != nullname = "Maria Sanchez" 而不是 sName = "Maria Sanchez"

  • 對於集合,避免在名稱中新增型別,例如 list、array、queue。使用內容的複數形式名稱。例如,對於汽車陣列,使用 cars 而不是 carArraycarList。可能存在例外情況,例如當您想展示功能的抽象形式而不涉及特定應用程式的上下文時。

  • 對於原始值,請使用 駝峰命名法,以小寫字元開頭。不要使用 _。酌情使用簡潔、易讀和語義化的名稱。例如,使用 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 文件中推薦和不推薦的做法

  • 如果變數不會被重新賦值,優先使用 const,如下所示

    js
    const name = "Shilpa";
    console.log(name);
    
  • 如果您要更改變數的值,請使用 let,如下所示

    js
    let age = 40;
    age++;
    console.log("Happy birthday!");
    
  • 以下示例在應該使用 const 的地方使用了 let。程式碼將工作,但我們希望在 MDN Web 文件程式碼示例中避免這種用法。

    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 強制值轉換為字串。改為使用不帶 newNumber()String()。寫

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 的非詳盡列表

  • 使用 fetch() 代替 XHR(XMLHttpRequest)。
  • 在 Web Audio API 中,使用 AudioWorklet 代替 ScriptProcessorNode

使用安全可靠的 API