編寫 JavaScript 程式碼示例的指南
以下指南涵蓋了為 MDN Web 文件編寫 JavaScript 示例程式碼。本文列出了編寫簡潔示例的規則,以使盡可能多的人能夠理解。
JavaScript 程式碼示例通用指南
本節解釋了編寫 JavaScript 程式碼示例時要記住的通用指南。後面的章節將涵蓋更具體的細節。
選擇格式
關於正確縮排、空格和行長度的看法一直備受爭議。關於這些話題的討論會分散建立和維護內容的注意力。
在 MDN Web 文件中,我們使用 Prettier 作為程式碼格式化程式,以保持程式碼風格一致(並避免離題討論)。您可以查閱我們的 配置檔案 以瞭解當前規則,並閱讀 Prettier 文件。
Prettier 格式化所有程式碼並保持風格一致。然而,還有一些額外的規則需要您遵循。
在支援的情況下使用現代 JavaScript 功能
一旦所有主流瀏覽器——Chrome、Edge、Firefox 和 Safari——都支援新功能(也稱為 Baseline),您就可以使用它們。
此規則不適用於頁面上正在記錄的 JavaScript 功能(它由 收錄標準 決定)。例如,您可以記錄 非標準或實驗性 功能,並編寫完整的示例來演示其行為,但您應避免在其他不相關功能的演示中使用這些功能,例如 Web API。
陣列
陣列建立
對於建立陣列,請使用字面量而不是建構函式。
像這樣建立陣列
const visitedCities = [];
建立陣列時不要這樣做
const visitedCities = new Array(length);
專案新增
向陣列新增專案時,請使用 push() 而不是直接賦值。考慮以下陣列
const pets = [];
像這樣向陣列新增專案
pets.push("cat");
不要像這樣向陣列新增專案
pets[pets.length] = "cat";
非同步方法
編寫非同步程式碼可提高效能,應儘可能使用。特別是,您可以使用
當兩種技術都可行時,我們更喜歡使用更簡單的 async/await 語法。不幸的是,除非您在 ECMAScript 模組中,否則不能在頂層使用 await。Node.js 使用的 CommonJS 模組不是 ES 模組。如果您的示例旨在在任何地方使用,請避免頂層 await。
註釋
註釋對於編寫好的程式碼示例至關重要。它們闡明瞭程式碼的意圖並幫助開發人員理解它。請特別注意它們。
-
如果程式碼的目的或邏輯不明顯,請新增帶有您意圖的註釋,如下所示
jslet total = 0; // Calculate the sum of the four first elements of arr for (let i = 0; i < 4; i++) { total += arr[i]; }另一方面,用散文重述程式碼並不是註釋的好用途
jslet total = 0; // For loop from 1 to 4 for (let i = 0; i < 4; i++) { // Add value to the total total += arr[i]; } -
當函式具有明確描述其功能的名稱時,註釋也不是必需的。寫
jscloseConnection();不要寫
jscloseConnection(); // Closing the connection
使用單行註釋
單行註釋用 // 標記,而不是用 /* … */ 括起來的塊註釋。
通常,使用單行註釋來註釋程式碼。作者必須用 // 標記註釋的每一行,以便更容易在視覺上注意到被註釋掉的程式碼。此外,此約定允許在除錯時使用 /* … */ 註釋掉程式碼段。
-
在斜槓和註釋之間留一個空格。以大寫字母開頭,像一個句子,但不要以句號結束註釋。
js// This is a well-written single-line comment -
如果註釋不是緊接在新縮排級別之後開始,請新增一個空行,然後添加註釋。這將建立一個程式碼塊,使註釋所指的內容顯而易見。此外,將您的註釋放在它們所指的程式碼之前的單獨行上。這在以下示例中顯示
jsfunction 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()或類似函式來輸出重要值。為了幫助讀者在不執行程式碼的情況下理解會發生什麼,您可以在函式之後新增一個註釋,其中包含將生成的日誌。寫jsfunction exampleFunc(fruitBasket) { console.log(fruitBasket); // ['banana', 'mango', 'orange'] }不要寫
jsfunction exampleFunc(fruitBasket) { // Logs: ['banana', 'mango', 'orange'] console.log(fruitBasket); } -
如果行太長,請將註釋放在函式後面,如下所示
jsfunction exampleFunc(fruitBasket) { console.log(fruitBasket); // ['banana', 'mango', 'orange', 'apple', 'pear', 'durian', 'lemon'] }
多行註釋
短註釋通常更好,因此請儘量將它們保持在 60-80 個字元的一行中。如果不可能,請在每行開頭使用 //
// 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
不要使用 /* … */
/* 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 中,您應該將省略號(…)放在註釋中。如果可能,請指示重用此程式碼段的人員期望新增什麼操作。
使用註釋表示省略號(…)更明確,可以防止開發人員複製貼上示例程式碼時出現錯誤。寫
function exampleFunc() {
// Add your code here
// …
}
不要像這樣使用省略號(…)
function exampleFunc() {
…
}
註釋掉引數
編寫程式碼時,您通常會省略不需要的引數。但在某些程式碼示例中,您想演示您沒有使用某些可能的引數。
為此,請在引數列表中使用 /* … */。這是僅使用單行註釋(//)的規則的一個例外。
array.forEach((value /* , index, array */) => {
// …
});
函式
函式名稱
對於函式名稱,請使用 駝峰命名法,以小寫字元開頭。酌情使用簡潔、易讀和語義化的名稱。
以下是函式名稱的正確示例
function sayHello() {
console.log("Hello!");
}
不要使用這樣的函式名稱
function SayHello() {
console.log("Hello!");
}
function doIt() {
console.log("Hello!");
}
函式宣告
-
如果可能,使用函式宣告而不是函式表示式來定義函式。
這是宣告函式的推薦方法
jsfunction sum(a, b) { return a + b; }這不是定義函式的好方法
jslet sum = function (a, b) { return a + b; }; -
當使用匿名函式作為回撥(傳遞給另一個方法呼叫的函式)時,如果您不需要訪問
this,請使用箭頭函式使程式碼更短、更簡潔。這是推薦的方法
jsconst array = [1, 2, 3, 4]; const sum = array.reduce((a, b) => a + b);而不是這樣
jsconst array = [1, 2, 3, 4]; const sum = array.reduce(function (a, b) { return a + b; }); -
考慮避免使用箭頭函式將函式賦值給識別符號。特別是,不要將箭頭函式用於方法。使用帶有關鍵字
function的函式宣告jsfunction x() { // … }不要這樣做
jsconst x = () => { // … }; -
當使用箭頭函式時,如果可能,請使用 隱式返回(也稱為 表示式體)
jsarr.map((e) => e.id);而不是
jsarr.map((e) => { return e.id; });
迴圈和條件語句
迴圈初始化
當需要 迴圈 時,從 for(;;)、for...of、while 等中選擇合適的。
-
遍歷所有集合元素時,避免使用經典的
for (;;)迴圈;更喜歡for...of或forEach()。請注意,如果您使用的是非Array的集合,則必須檢查是否實際支援for...of(它要求變數是可迭代的),或者是否實際存在forEach()方法。使用
for...ofjsconst dogs = ["Rex", "Lassie"]; for (const dog of dogs) { console.log(dog); }或
forEach()jsconst dogs = ["Rex", "Lassie"]; dogs.forEach((dog) => { console.log(dog); });不要使用
for (;;)——您不僅需要新增一個額外的索引i,而且還需要跟蹤陣列的長度。這對於初學者來說容易出錯。jsconst dogs = ["Rex", "Lassie"]; for (let i = 0; i < dogs.length; i++) { console.log(dogs[i]); } -
確保透過為
for...of使用const關鍵字或為其他迴圈使用let來正確定義初始化程式。不要省略它。這些是正確的示例jsconst cats = ["Athena", "Luna"]; for (const cat of cats) { console.log(cat); } for (let i = 0; i < 4; i++) { result += arr[i]; }以下示例不遵循初始化推薦指南(它隱式建立全域性變數並在嚴格模式下失敗)
jsconst cats = ["Athena", "Luna"]; for (i of cats) { console.log(i); } -
當您需要同時訪問值和索引時,您可以使用
.forEach()而不是for (;;)。寫jsconst gerbils = ["Zoé", "Chloé"]; gerbils.forEach((gerbil, i) => { console.log(`Gerbil #${i}: ${gerbil}`); });不要寫
jsconst 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 語句之後繼續。寫
if (test) {
// Perform something if test is true
// …
return;
}
// Perform something if test is false
// …
不要寫
if (test) {
// Perform something if test is true
// …
return;
} else {
// Perform something if test is false
// …
}
在控制流語句和迴圈中使用大括號
雖然像 if、for 和 while 這樣的控制流語句在內容由一個語句組成時不需要使用大括號,但您應該始終使用大括號。寫
for (const car of storedCars) {
car.paint("red");
}
不要寫
for (const car of storedCars) car.paint("red");
這可以防止在新增更多語句時忘記新增大括號。
Switch 語句
Switch 語句可能有點棘手。
-
在特定情況下,不要在
return語句之後新增break語句。相反,像這樣編寫return語句jsswitch (species) { case "chicken": return farm.shed; case "horse": return corral.entry; default: return ""; }如果您新增
break語句,它將不可達。不要寫jsswitch (species) { case "chicken": return farm.shed; break; case "horse": return corral.entry; break; default: return ""; } -
將
default作為最後一個情況,並且不要以break語句結束。如果您需要以不同方式執行此操作,請添加註釋解釋原因。 -
請記住,當您為案例宣告區域性變數時,您需要使用大括號來定義作用域
jsswitch (fruits) { case "Orange": { const slice = fruit.slice(); eat(slice); break; } case "Apple": { const core = fruit.extractCore(); recycle(core); break; } }
錯誤處理
-
如果您的程式的某些狀態丟擲未捕獲的錯誤,它們將停止執行並可能降低示例的有用性。因此,您應該使用
try...catch塊捕獲錯誤,如下所示jstry { console.log(getResult()); } catch (e) { console.error(e); } -
當您不需要
catch語句的引數時,請省略它jstry { console.log(getResult()); } catch { console.error("An error happened!"); }
注意:請記住,只有 可恢復的 錯誤才應被捕獲和處理。所有不可恢復的錯誤都應被放行並冒泡到呼叫堆疊。
物件
物件名稱
-
定義類時,類名使用 PascalCase(以大寫字母開頭),物件屬性和方法名使用 camelCase(以小寫字母開頭)。
-
定義物件例項時,無論是字面量還是透過建構函式,例項名都使用 camelCase,以小寫字母開頭。例如
jsconst hanSolo = new Person("Han Solo", 25, "he/him"); const luke = { name: "Luke Skywalker", age: 25, pronouns: "he/him", };
物件建立
對於建立一般物件(即不涉及類時),請使用字面量而不是建構函式。
例如,這樣做
const object = {};
不要像這樣建立一般物件
const object = new Object();
物件類
-
使用 ES 類語法定義物件,而不是舊式建構函式。
例如,這是推薦的方法
jsclass Person { constructor(name, age, pronouns) { this.name = name; this.age = age; this.pronouns = pronouns; } greeting() { console.log(`Hi! I'm ${this.name}`); } } -
使用
extends進行繼承jsclass Teacher extends Person { // … }
方法
要定義方法,請使用方法定義語法
const obj = {
foo() {
// …
},
bar() {
// …
},
};
而不是
const obj = {
foo: function () {
// …
},
bar: function () {
// …
},
};
物件屬性
-
Object.prototype.hasOwnProperty()方法已被棄用,取而代之的是Object.hasOwn()。 -
如果可能,使用簡寫,避免重複屬性識別符號。寫
jsfunction createObject(name, age) { return { name, age }; }不要寫
jsfunction createObject(name, age) { return { name: name, age: age }; }
運算子
本節列出了我們關於何時使用哪些運算子的建議。
條件運算子
當您想根據條件將字面量值儲存到變數中時,請使用 條件(三元)運算子 而不是 if...else 語句。此規則也適用於返回值。寫
const x = condition ? 1 : 2;
不要寫
let x;
if (condition) {
x = 1;
} else {
x = 2;
}
條件運算子在建立字串以記錄資訊時很有用。在這種情況下,使用常規的 if...else 語句會導致用於日誌記錄等附帶操作的長程式碼塊,從而混淆示例的中心點。
嚴格相等運算子
優先使用 嚴格相等(三等號)和不相等運算子,而不是寬鬆相等(雙等號)和不相等運算子。
像這樣使用嚴格相等和不相等運算子
name === "Shilpa";
age !== 25;
不要像下面這樣使用寬鬆相等和不相等運算子
name == "Shilpa";
age != 25;
如果您需要使用 == 或 !=,請記住 == null 是唯一可接受的情況。由於 TypeScript 會在所有其他情況下失敗,我們不希望它們出現在我們的示例程式碼中。考慮添加註釋以解釋您為什麼需要它。
布林測試的快捷方式
優先使用布林測試的快捷方式。例如,使用 if (x) 和 if (!x),而不是 if (x === true) 和 if (x === false),除非不同型別的 truthy 或 falsy 值需要區別處理。
字串
字串字面量可以用單引號括起來,如 'A string',也可以用雙引號括起來,如 "A string"。不用擔心使用哪一個;Prettier 會保持一致。
模板字面量
對於將值插入字串,請使用 模板字面量。
-
這是一個推薦使用模板字面量的示例。它們的使用可以防止許多空格錯誤。
jsconst name = "Shilpa"; console.log(`Hi! I'm ${name}!`);不要像這樣連線字串
jsconst name = "Shilpa"; console.log("Hi! I'm" + name + "!"); // Hi! I'mShilpa! -
不要過度使用模板字面量。如果沒有替換,請改用普通字串字面量。
變數
變數名稱
良好的變數名稱對於理解程式碼至關重要。
-
使用簡短的識別符號,並避免不常見的縮寫。良好的變數名稱通常在 3 到 10 個字元長之間,但這只是一個提示。例如,
accelerometer比為了字元長度而縮寫為acclmtr更具描述性。 -
嘗試使用與現實世界相關的示例,其中每個變數都具有清晰的語義。僅當示例簡單且不自然時才回退到佔位符名稱,如
foo和bar。 -
不要使用 匈牙利命名法 命名約定。不要在變數名前加上其型別。例如,寫
bought = car.buyer !== null而不是bBought = oCar.sBuyer != null或name = "Maria Sanchez"而不是sName = "Maria Sanchez"。 -
對於集合,避免在名稱中新增型別,例如 list、array、queue。使用內容的複數形式名稱。例如,對於汽車陣列,使用
cars而不是carArray或carList。可能存在例外情況,例如當您想展示功能的抽象形式而不涉及特定應用程式的上下文時。 -
對於原始值,請使用 駝峰命名法,以小寫字元開頭。不要使用
_。酌情使用簡潔、易讀和語義化的名稱。例如,使用currencyName而不是currency_name。 -
避免使用冠詞和所有格。例如,使用
car而不是myCar或aCar。可能存在例外情況,例如在沒有實際上下文的情況下描述功能時。 -
像這樣使用變數名稱
jsconst playerScore = 0; const speed = distance / time;不要像這樣命名變數
jsconst thisIsaveryLONGVariableThatRecordsPlayerscore345654 = 0; const s = d / t;
注意:唯一允許不使用易讀、語義化名稱的地方是存在非常普遍認可的約定,例如將 i 和 j 用於迴圈迭代器。
變數宣告
宣告變數和常量時,請使用 let 和 const 關鍵字,而不是 var。以下示例顯示了 MDN Web 文件中推薦和不推薦的做法
-
如果變數不會被重新賦值,優先使用
const,如下所示jsconst name = "Shilpa"; console.log(name); -
如果您要更改變數的值,請使用
let,如下所示jslet age = 40; age++; console.log("Happy birthday!"); -
以下示例在應該使用
const的地方使用了let。程式碼將工作,但我們希望在 MDN Web 文件程式碼示例中避免這種用法。jslet name = "Shilpa"; console.log(name); -
以下示例對一個被重新賦值的變數使用了
const。重新賦值將丟擲錯誤。jsconst age = 40; age++; console.log("Happy birthday!"); -
以下示例使用
var,汙染了全域性作用域jsvar age = 40; var name = "Shilpa"; -
每行宣告一個變數,如下所示
jslet var1; let var2; let var3 = "Apapou"; let var4 = var3;不要在一行中宣告多個變數,用逗號分隔或使用鏈式宣告。避免像這樣宣告變數
jslet var1, var2; let var3 = var4 = "Apapou"; // var4 is implicitly created as a global variable; fails in strict mode
型別轉換
避免隱式型別強制轉換。特別是,避免使用 +val 強制值轉換為數字,避免使用 "" + val 強制值轉換為字串。改為使用不帶 new 的 Number() 和 String()。寫
class Person {
#name;
#birthYear;
constructor(name, year) {
this.#name = String(name);
this.#birthYear = Number(year);
}
}
不要寫
class Person {
#name;
#birthYear;
constructor(name, year) {
this.#name = "" + name;
this.#birthYear = +year;
}
}
要避免的 Web API
除了這些 JavaScript 語言特性之外,我們還建議一些與 Web API 相關的指南。
避免瀏覽器字首
如果所有主流瀏覽器(Chrome、Edge、Firefox 和 Safari)都支援某項功能,請不要給該功能加字首。寫
const context = new AudioContext();
避免字首帶來的額外複雜性。不要寫
const AudioContext = window.AudioContext || window.webkitAudioContext;
const context = new AudioContext();
同樣的規則也適用於 CSS 字首。
避免使用已棄用的 API
當一個方法、屬性或整個介面被棄用時,請勿使用它(除了其文件)。而是使用現代 API。
以下是要避免和替換的 Web API 的非詳盡列表
- 使用
fetch()代替 XHR(XMLHttpRequest)。 - 在 Web Audio API 中,使用
AudioWorklet代替ScriptProcessorNode。
使用安全可靠的 API
-
不要使用
Element.innerHTML將純文字內容插入元素;改用Node.textContent。如果開發人員不控制引數,屬性innerHTML會導致安全問題。作為作者,我們越避免使用它,開發人員複製貼上我們的程式碼時產生的安全漏洞就越少。以下示例演示了
textContent的使用。jsconst text = "Hello to all you good people"; const para = document.createElement("p"); para.textContent = text;不要使用
innerHTML將 純文字 插入 DOM 節點。jsconst text = "Hello to all you good people"; const para = document.createElement("p"); para.innerHTML = text; -
alert()函式不可靠。它在 MDN Web 文件中位於內的即時示例中不起作用。此外,它對整個視窗都是模態的,這很煩人。在靜態程式碼示例中,使用console.log()或console.error()。在 即時示例 中,避免使用console.log()和console.error(),因為它們不顯示。使用專用的 UI 元素。
使用適當的日誌方法
- 記錄訊息時,使用
console.log()。 - 記錄錯誤時,使用
console.error()。
另見
JavaScript 語言參考 - 瀏覽我們的 JavaScript 參考頁面,檢視一些優秀、簡潔、有意義的 JavaScript 程式碼片段。