编写 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...of
jsconst 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 代码片段。