编写 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