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 代码片段。