# JavaScript 编码规范

# 1 前言

本文档的目标是使 JavaScript 代码风格保持一致,容易被理解和被维护,主要用在浏览器端。

本文档参考了一些外部团队开源的代码规范,实现之处如有不便还请联系作者进行改善。

# 2 规范说明

目前 JS 开发中大部分使用 ECMAScript5 的语法,而 ES6 也颁布许久并且目前已经得到广泛浏览器的支持。ES6 在语法特性上更为简洁标准,并且修复了一些由于语言特性容易引起的 bug(比如箭头函数自带this),因此建议在开发时统一使用 ES6 标准。推荐阅读阮一峰的 ES6 教程基础部分。

本节内容的大多要求可以由编辑器指定如Prettier,Eslint等代码格式化工具自动处理。一部分需要在编辑器中进行设定。

# 1. 引用

  • 1.1 【必须】 使用 const 定义你的所有引用;避免使用 var。 eslint: prefer-const, no-const-assign

    原因? 这样能够确保你不能重新赋值你的引用,否则可能导致错误或者产生难以理解的代码。

    // bad
    var a = 1;
    var b = 2;
    
    // good
    const a = 1;
    const b = 2;
    
    1
    2
    3
    4
    5
    6
    7
  • 1.2 【必须】 如果你必须重新赋值你的引用, 使用 let 代替 var。 eslint: no-var

    原因? let 是块级作用域,而不像 var 是函数作用域。

    // bad
    var count = 1;
    if (true) {
      count += 1;
    }
    
    // good, use the let.
    let count = 1;
    if (true) {
      count += 1;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 2. 对象

  • 2.1 【必须】 使用字面量语法创建对象。 eslint: no-new-object

    // bad
    const item = new Object();
    
    // good
    const item = {};
    
    1
    2
    3
    4
    5
  • 2.2 【必须】 只使用引号标注无效标识符的属性。 eslint: quote-props

    原因? 一般来说,我们认为这样更容易阅读。 它能更好地适配语法高亮显示功能,并且更容易通过许多 JS 引擎进行优化。

    // bad
    const bad = {
      'foo': 3,
      'bar': 4,
      'data-blah': 5,
    };
    
    // good
    const good = {
      foo: 3,
      bar: 4,
      'data-blah': 5,
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 3. 数组

  • 3.1 【必须】 使用字面量语法创建数组。 eslint: no-array-constructor

    // bad
    const items = new Array();
    
    // good
    const items = [];
    
    1
    2
    3
    4
    5
  • 3.2 【必须】 使用 Array#push 代替直接赋值来给数组添加项。

    const someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
    
    1
    2
    3
    4
    5
    6
    7
  • 3.3 【必须】 使用数组展开符 ... 来拷贝数组。

    // bad
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i += 1) {
      itemsCopy[i] = items[i];
    }
    
    // good
    const itemsCopy = [...items];
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 3.4 【必须】 使用 Array.from 将一个类数组(array-like)对象转换成一个数组。

    const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
    
    // bad
    const arr = Array.prototype.slice.call(arrLike);
    
    // good
    const arr = Array.from(arrLike);
    
    1
    2
    3
    4
    5
    6
    7
  • 3.5 【必须】 使用 Array.from 代替展开符 ... 映射迭代器,因为它避免了创建一个中间数组。

    // bad
    const baz = [...foo].map(bar);
    
    // good
    const baz = Array.from(foo, bar);
    
    1
    2
    3
    4
    5

# 4. 解构

  • 4.1 【必须】 在有多个返回值时, 使用对象解构,而不是数组解构。

    原因? 你可以随时添加新的属性或者改变属性的顺序,而不用修改调用方。

    // bad
    function processInput(input) {
      // 处理代码...
      return [left, right, top, bottom];
    }
    
    // 调用者需要考虑返回数据的顺序。
    const [left, __, top] = processInput(input);
    
    // good
    function processInput(input) {
      // 处理代码...
      return { left, right, top, bottom };
    }
    
    // 调用者只选择他们需要的数据。
    const { left, top } = processInput(input);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 5. 字符

  • 5.1 【必须】 不应该用字符串跨行连接符的格式来跨行编写,这样会使当前行长度超过100个字符。

    原因? 断开的字符串维护起来很痛苦,并且会提高索引难度。

    // bad
    const errorMessage = 'This is a super long error that was thrown because \
    of Batman. When you stop to think about how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // bad
    const errorMessage = 'This is a super long error that was thrown because ' +
      'of Batman. When you stop to think about how Batman had anything to do ' +
      'with this, you would get nowhere fast.';
    
    // good
    const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • 5.2 【必须】 构建字符串时,使用字符串模板代替字符串拼接。 eslint: prefer-template template-curly-spacing

    原因? 字符串模板为您提供了一种可读的、简洁的语法,具有正确的换行和字符串插值特性。

    // bad
    function sayHi(name) {
      return 'How are you, ' + name + '?';
    }
    
    // bad
    function sayHi(name) {
      return ['How are you, ', name, '?'].join();
    }
    
    // bad
    function sayHi(name) {
      return `How are you, ${ name }?`;
    }
    
    // good
    function sayHi(name) {
      return `How are you, ${name}?`;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 5.3 【必须】 永远不要使用 eval() 执行放在字符串中的代码,它导致了太多的漏洞。 eslint: no-eval

  • 5.4 【必须】 不要在字符串中转义不必要的字符。 eslint: no-useless-escape

    原因? 反斜杠损害了可读性,因此只有在必要的时候才可以出现。

    // bad
    const foo = '\'this\' \i\s \"quoted\"';
    
    // good
    const foo = '\'this\' is "quoted"';
    const foo = `my name is '${name}'`;
    
    1
    2
    3
    4
    5
    6

# 6. 函数

  • 6.1 【必须】 把立即执行函数包裹在圆括号里。 eslint: wrap-iife

    原因? 立即调用的函数表达式是个独立的单元 - 将它和它的调用括号还有入参包装在一起可以非常清晰的表明这一点。请注意,在一个到处都是模块的世界中,您几乎用不到 IIFE。

    // immediately-invoked function expression (IIFE) 立即调用的函数表达式
    (function () {
      console.log('Welcome to the Internet. Please follow me.');
    }());
    
    1
    2
    3
    4
  • 6.2 【必须】 切记不要在非功能块中声明函数 (if, while, 等)。 请将函数赋值给变量。 浏览器允许你这样做, 但是不同浏览器会有不同的行为, 这并不是什么好事。 eslint: no-loop-func

  • 6.3 【必须】 ECMA-262 将 block 定义为语句列表。 而函数声明并不是语句。

    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    let test;
    if (currentUser) {
      test = () => {
        console.log('Yup.');
      };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 6.4 【必须】 永远不要给一个参数命名为 arguments。 这将会覆盖函数默认的 arguments 对象。

    // bad
    function foo(name, options, arguments) {
      // ...
    }
    
    // good
    function foo(name, options, args) {
      // ...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 6.5 【必须】 使用默认参数时避免副作用。

    原因? 他们很容易混淆。

    var b = 1;
    // bad
    function count(a = b++) {
      console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 6.6 【必须】 函数声明语句中需要空格。 eslint: space-before-function-paren space-before-blocks

    原因? 一致性很好,在删除或添加名称时不需要添加或删除空格。

    // bad
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // good
    const x = function () {};
    const y = function a() {};
    
    1
    2
    3
    4
    5
    6
    7
    8

# 7. 类和构造器

  • 7.1 【必须】 避免定义重复的类成员。 eslint: no-dupe-class-members

    原因? 重复的类成员声明将会默认使用最后一个 - 具有重复的类成员可以说是一个bug。

    // bad
    class Foo {
      bar() { return 1; }
      bar() { return 2; }
    }
    
    // good
    class Foo {
      bar() { return 1; }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 8. 模块

  • 8.1 【必须】 对于同一个路径,只在一个地方引入所有需要的东西。 eslint: no-duplicates

    原因? 对于同一个路径,如果存在多行引入,会使代码更难以维护。

    // bad
    import foo from 'foo';
    // … 其他导入 … //
    import { named1, named2 } from 'foo';
    
    // good
    import foo, { named1, named2 } from 'foo';
    
    // good
    import foo, {
      named1,
      named2,
    } from 'foo';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • 8.2 【必须】 将所有的 imports 语句放在其他语句之前。 eslint: import/first

    原因? 将所有的 imports 提到顶部,可以防止某些诡异行为的发生。

    // bad
    import foo from 'foo';
    foo.init();
    
    import bar from 'bar';
    
    // good
    import foo from 'foo';
    import bar from 'bar';
    
    foo.init();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 9. 变量

  • 9.1 【必须】 变量应先声明再使用,禁止引用任何未声明的变量,除非你明确知道引用的变量存在于当前作用域链上。禁止不带任何关键词定义变量,这样做将会创建一个全局变量,污染全局命名空间,造成程序意料之外的错误。 eslint: no-undef prefer-const

    // bad, 这会创建一个全局变量
    superPower = new SuperPower();
    
    // good
    const superPower = new SuperPower();
    
    // bad, 容易污染外部变量
    let superPower = 'a';
    (function() {
      superPower = 'b';
    })();
    console.log(superPower);
    
    // good
    let superPower = 'a';
    (function() {
      let superPower = 'b';
    })();
    console.log(superPower);
    
    // bad, 更常见的情况是这样的,在 for 循环里的 i 将会污染外部的变量 i
    let i = 1;
    (function() {
      for (i = 0; i < 10; i++) {
        console.log('inside', i);
      }
    
      console.log('outside', i)
    })();
    console.log('global', i);
    
    // good
    let i = 1;
    (function() {
      // i 的作用域在 for 循环内
      for (let i = 0; i < 10; i++) {
        console.log('inside i', i);
      }
    
      // 如果真的需要在 for 循环外使用循环变量,应该先定义在外部
      let j;
    
      for (j = 0; j < 10; j++) {
        console.log('inside j:', j);
      }
    
      console.log('outside j', j);
    })();
    console.log('global', i);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
  • 9.2 【必须】 不要链式变量赋值。 eslint: no-multi-assign

    原因? 链式变量赋值会创建隐式全局变量。

    // bad
    (function example() {
      /**
      * JavaScript 把它解释为
      * let a = ( b = ( c = 1 ) );
      * let 关键词只适用于变量 a,变量 b 和变量 c 则变成了全局变量。
      */
      let a = b = c = 1;
    }());
    
    // throws ReferenceError
    console.log(a);
    // 1
    console.log(b);
    // 1
    console.log(c);
    
    // good
    (function example() {
      let a = 1;
      let b = a;
      let c = a;
    }());
    
    // throws ReferenceError
    console.log(a);
    // throws ReferenceError
    console.log(b);
    // throws ReferenceError
    console.log(c);
    
    // 对于 `const` 也一样
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  • 9.3 【必须】 避免使用不必要的递增和递减操作符 (++, --)。 eslint no-plusplus

    原因? 在eslint文档中,一元操作符 ++--会自动添加分号,不同的空白可能会改变源代码的语义。建议使用 num += 1 这样的语句来做递增和递减,而不是使用 num++num ++ 。同时 ++numnum++ 的差异也使代码的可读性变差。不必要的增量和减量语句会导致无法预先明确递增/预递减值,这可能会导致程序中的意外行为。

    但目前依然允许在 for loop 中使用 ++-- 的语法,但依然建议尽快迁移到 += 1-= 1 的语法。 #22

    // bad, i = 11, j = 20
    let i = 10;
    let j = 20;
    i ++
    j
    
    // bad, i = 10, j = 21
    let i = 10;
    let j = 20;
    i
    ++
    j
    
    // bad
    const array = [1, 2, 3];
    let num = 1;
    num++;
    --num;
    
    // not good, just acceptable for upforward compatible.
    let sum = 0;
    let truthyCount = 0;
    for (let i = 0; i < array.length; i++) {
      let value = array[i];
      sum += value;
      if (value) {
        truthyCount++;
      }
    }
    
    // good
    const array = [1, 2, 3];
    let num = 1;
    num += 1;
    num -= 1;
    
    // good
    const sum = array.reduce((a, b) => a + b, 0);
    const truthyCount = array.filter(Boolean).length;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
  • 9.4 【必须】 避免在赋值语句 = 前后换行。如果你的代码单行长度超过了 max-len 定义的长度而不得不换行,那么使用括号包裹。 eslint operator-linebreak.

    原因? 在 = 前后换行,可能混淆赋的值。

    // bad
    const foo
      = 'superLongLongLongLongLongLongLongLongString';
    
    // bad
    const bar =
      superLongLongLongLongLongLongLongLongFunctionName();
    
    // bad
    const fullHeight = borderTop +
                   innerHeight +
                   borderBottom;  
    
    // bad
    const anotherHeight = borderTop +
      innerHeight +
      borderBottom;
    
    // bad
    const thirdHeight = (
      borderTop +
      innerHeight +
      borderBottom
    );
    
    // good - max-len 会忽略字符串,直接写后面即可。
    const foo = 'superLongLongLongLongLongLongLongLongString';
    
    // good
    const bar = (
      superLongLongLongLongLongLongLongLongFunctionName()
    );
    
    // good
    const fullHeight = borderTop
                     + innerHeight
                     + borderBottom;
    
    // good
    const anotherHeight = borderTop
      + innerHeight
      + borderBottom;
    
    // good
    const thirdHeight = (
      borderTop
      + innerHeight
      + borderBottom
    );
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
  • 9.5 【必须】 禁止定义了变量却不使用它。 eslint: no-unused-vars

    原因? 在代码里到处定义变量却没有使用它,不完整的代码结构看起来像是个代码错误。即使没有使用,但是定义变量仍然需要消耗资源,并且对阅读代码的人也会造成困惑,不知道这些变量是要做什么的。

    // bad
    let some_unused_var = 42;
    
    // bad,定义了变量不意味着就是使用了
    let y = 10;
    y = 5;
    
    // bad,对自身的操作并不意味着使用了
    let z = 0;
    z = z + 1;
    
    // bad, 未使用的函数参数
    function getX(x, y) {
        return x;
    }
    
    // good
    function getXPlusY(x, y) {
      return x + y;
    }
    
    let x = 1;
    let y = a + 2;
    
    alert(getXPlusY(x, y));
    
    /**
    * 有时候我们想要提取某个对象排除了某个属性外的其他属性,会用 rest 参数解构对象
    * 这时候 type 虽然未使用,但是仍然被定义和赋值,这也是一种空间的浪费
    * type 的值是 'a'
    * coords 的值是 data 对象,但是没有 type 属性 { example1: 'b', example2: 'c' }
    */
    let data = { type: 'a', example1: 'b', example2: 'c' }
    let { type, ...coords } = data;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

# 10. 比较运算符和等号

  • 10.1 【必须】casedefault 的子句中,如果存在声明 (例如. let, const, function, 和 class),使用大括号来创建块级作用域。 eslint: no-case-declarations

    原因? 变量声明的作用域在整个 switch 语句内,但是只有在 case 条件为真时变量才会被初始化。 当多个 case 语句定义相同的变量时,就会导致变量覆盖的问题。

    // bad
    switch (foo) {
      case 1:
        let x = 1;
        break;
      case 2:
        const y = 2;
        break;
      case 3:
        function f() {
          // ...
        }
        break;
      default:
        class C {}
    }
    
    // good
    switch (foo) {
      case 1: {
        let x = 1;
        break;
      }
      case 2: {
        const y = 2;
        break;
      }
      case 3: {
        function f() {
          // ...
        }
        break;
      }
      case 4:
        bar();
        break;
      default: {
        class C {}
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  • 10.2 【必须】 使用混合运算符时,使用小括号括起来需要一起计算的部分,只要觉得有必要,那么尽可能地用括号让代码的优先级更明显。 eslint: no-mixed-operators

    原因? 这能提高可读性并且表明开发人员的意图。

    // bad
    const foo = a && b < 0 || c > 0 || d + 1 === 0;
    
    // bad
    const bar = a ** b - 5 % d;
    
    // bad, 可能陷入一种 (a || b) && c 的思考
    if (a || b && c) {
      return d;
    }
    
    // good
    const foo = (a && b < 0) || (c > 0) || (d + 1 === 0);
    
    // good
    const bar = (a ** b) - (5 % d);
    
    // good
    if (a || (b && c)) {
      return d;
    }
    
    // good
    const bar = a + (b / c * d);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

# 11. 代码块

  • 11.1 【必须】 当有多行代码块的时候,应使用大括号包裹。 eslint: nonblock-statement-body-position

    // bad
    if (test)
      return false;
    
    // bad
    let condition = true;
    let test = 1;
    // 在缩进不规范的时候,容易造成误解
    if (condition)
      condition = false;
      test = 2;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function foo() { return false; }
    
    // good
    function bar() {
      return false;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  • 11.2 【必须】 如果你使用的是 ifelse 的多行代码块,则将 else 语句放在 if 块闭括号同一行的位置。 eslint: brace-style

    // bad
    if (test) {
      thing1();
      thing2();
    }
    else {
      thing3();
    }
    
    // good
    if (test) {
      thing1();
      thing2();
    } else {
      thing3();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# 12. 控制语句

  • 12.1 【必须】 不要使用选择操作符代替控制语句。

    // bad
    !isRunning && startRunning();
    
    // good
    if (!isRunning) {
      startRunning();
    }
    
    1
    2
    3
    4
    5
    6
    7

# 13. 注释

  • 13.1 【必须】 使用 /** ... */ 来进行多行注释。

    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
      * based on the passed-in tag name
      */
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  • 13.2 【必须】 用一个空格开始所有的注释,使它更容易阅读。 eslint: spaced-comment

    // bad
    //is current tab
    const active = true;
    
    // good
    // is current tab
    const active = true;
    
    // bad
    /**
     *make() returns a new element
      *based on the passed-in tag name
      */
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
      * based on the passed-in tag name
      */
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

# 14. 空白

  • 14.1 【必须】 在控制语句中的左括号前放置1个空格(if,while等)。在函数调用和声明中,参数列表和函数名之间不能留空格。keyword-spacing

    // bad
    if(isJedi) {
      fight ();
    }
    
    // good
    if (isJedi) {
      fight();
    }
    
    // bad
    function fight () {
      console.log ('Swooosh!');
    }
    
    // good
    function fight() {
      console.log('Swooosh!');
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 14.2 【必须】 运算符左右设置各设置一个空格 eslint: space-infix-ops

    // bad
    const x=y+5;
    
    // good
    const x = y + 5;
    
    1
    2
    3
    4
    5
  • 14.3 【必须】 在文件的结尾需要保留一个空行 eslint: eol-last

    // bad
    import { es6 } from './AirbnbStyleGuide';
    // ...
    export default es6;
    
    1
    2
    3
    4
    // bad
    import { es6 } from './AirbnbStyleGuide';
    // ...
    export default es6;↵
    ↵
    
    1
    2
    3
    4
    5
    // good
    import { es6 } from './AirbnbStyleGuide';
    // ...
    export default es6;
    1
    2
    3
    4
  • 14.4 【必须】 不要在块的开头使用空白行。 eslint: padded-blocks

    // bad
    function bar() {
    
      console.log(foo);
    
    }
    
    // bad
    if (baz) {
    
      console.log(qux);
    } else {
      console.log(foo);
    
    }
    
    // bad
    class Foo {
    
      constructor(bar) {
        this.bar = bar;
      }
    }
    
    // good
    function bar() {
      console.log(foo);
    }
    
    // good
    if (baz) {
      console.log(qux);
    } else {
      console.log(foo);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
  • 14.5 【必须】 不要使用多个空行填充代码。 eslint: no-multiple-empty-lines

    // bad
    class Person {
      constructor(fullName, email, birthday) {
        this.fullName = fullName;
    
    
        this.email = email;
    
    
        this.setAge(birthday);
      }
    
    
      setAge(birthday) {
        const today = new Date();
    
    
        const age = this.getAge(today, birthday);
    
    
        this.age = age;
      }
    
    
      getAge(today, birthday) {
        // ..
      }
    }
    
    // good
    class Person {
      constructor(fullName, email, birthday) {
        this.fullName = fullName;
        this.email = email;
        this.setAge(birthday);
      }
    
      setAge(birthday) {
        const today = new Date();
        const age = getAge(today, birthday);
        this.age = age;
      }
    
      getAge(today, birthday) {
        // ..
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
  • 14.6 【必须】 不要在括号内添加空格。 eslint: space-in-parens

    // bad
    function bar( foo ) {
      return foo;
    }
    
    // good
    function bar(foo) {
      return foo;
    }
    
    // bad
    if ( foo ) {
      console.log(foo);
    }
    
    // good
    if (foo) {
      console.log(foo);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 14.7 【必须】 不要在中括号中添加空格。 eslint: array-bracket-spacing

    // bad
    const foo = [ 1, 2, 3 ];
    console.log(foo[ 0 ]);
    
    // good
    const foo = [1, 2, 3];
    console.log(foo[0]);
    
    1
    2
    3
    4
    5
    6
    7
  • 14.8 【必须】 避免让你的代码行超过120个字符(包括空格)。 注意:根据上边的规则,长字符串编写可不受该规则约束,不应该被分解。 eslint: max-len

    原因? 这样能够提升代码可读性和可维护性。

    // bad
    const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
    
    // bad
    $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
    
    // good
    const foo = jsonData
      && jsonData.foo
      && jsonData.foo.bar
      && jsonData.foo.bar.baz
      && jsonData.foo.bar.baz.quux
      && jsonData.foo.bar.baz.quux.xyzzy;
    
    // good
    $.ajax({
      method: 'POST',
      url: 'https://airbnb.com/',
      data: { name: 'John' },
    })
      .done(() => console.log('Congratulations!'))
      .fail(() => console.log('You have failed this city.'));
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • 14.9 【必须】 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距。 eslint: block-spacing

    // bad
    function foo() {return true;}
    if (foo) { bar = 0;}
    
    // good
    function foo() { return true; }
    if (foo) { bar = 0; }
    
    1
    2
    3
    4
    5
    6
    7
  • 14.10 【必须】 逗号之前避免使用空格,逗号之后需要使用空格。eslint: comma-spacing

    // bad
    const arr = [1 , 2];
    
    // good
    const arr = [1, 2];
    
    1
    2
    3
    4
    5
  • 14.11 【必须】 避免在函数名及其入参括号之间插入空格。 eslint: func-call-spacing

    // bad
    func ();
    
    func
    ();
    
    // good
    func();
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 14.12 【必须】 在对象的属性和值之间的冒号前不加空格,冒号后加空格。 eslint: key-spacing

    // bad
    var obj = { foo : 42 };
    var obj2 = { foo:42 };
    
    // good
    var obj = { foo: 42 };
    
    1
    2
    3
    4
    5
    6
  • 14.13 【必须】 避免在行尾添加空格。 eslint: no-trailing-spaces

  • 14.14 【必须】 在代码开始处不允许存在空行,行间避免出现多个空行,而结尾处必须保留一个空行。 eslint: no-multiple-empty-lines

    // bad
    const x = 1;
    
    
    
    const y = 2;
    
    // good
    const x = 1;
    
    const y = 2;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 15. 逗号

  • 15.1 【必须】 逗号不能前置 eslint: comma-style

    // bad
    const story = [
        once
      , upon
      , aTime
    ];
    
    // good
    const story = [
      once,
      upon,
      aTime,
    ];
    
    // bad
    const hero = {
        firstName: 'Ada'
      , lastName: 'Lovelace'
      , birthYear: 1815
      , superPower: 'computers'
    };
    
    // good
    const hero = {
      firstName: 'Ada',
      lastName: 'Lovelace',
      birthYear: 1815,
      superPower: 'computers',
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

# 16. jQuery

  • 16.1 【推荐】 对于 jQuery 对象一律使用 $ 符作为前缀。

    // bad
    const sidebar = $('.sidebar');
    
    // good
    const $sidebar = $('.sidebar');
    
    // good
    const $sidebarBtn = $('.sidebar-btn');
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 16.2 【推荐】 缓存 jQuery 查询,节省 DOM 查询开销。

    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...
    
      $('.sidebar').css({
        'background-color': 'pink',
      });
    }
    
    // good
    function setSidebar() {
      const $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...
    
      $sidebar.css({
        'background-color': 'pink',
      });
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • 16.3 【推荐】 能通过一次调用查找到的,不要分多次;在已有对象内查询,使用 find 函数,减少重复查询。

    // bad
    $('ul', '.sidebar').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good
    $sidebar.find('ul').hide();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# 17. 命名规范

  • 17.1 【必须】 使用驼峰命名法(camelCase)命名对象、函数和实例。 eslint: camelcase

    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 17.2 【必须】 只有在命名构造器或者类的时候,才用帕斯卡拼命名法(PascalCase),即首字母大写。 eslint: new-cap

    // bad
    function user(options) {
      this.name = options.name;
    }
    
    const bad = new user({
      name: 'nope',
    });
    
    // good
    class User {
      constructor(options) {
        this.name = options.name;
      }
    }
    
    const good = new User({
      name: 'yup',
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 17.3 【必须】 导出默认函数时使用驼峰命名法,并且文件名应该和方法名相同。文件名建议使用 kebab-case,后缀为小写。

    function makeStyleGuide() {
      // ...
    }
    
    export default makeStyleGuide;
    
    1
    2
    3
    4
    5
  • 17.4 【必须】 当导出构造器 / 类 / 单例 / 函数库 / 对象时应该使用帕斯卡命名法(首字母大写)。

    const TencentStyleGuide = {
      es6: {
      },
    };
    
    export default TencentStyleGuide;
    
    1
    2
    3
    4
    5
    6

# 18. 分号

  • 18.1 【必须】 要加分号 eslint: semi

    原因? 当 JavaScript 解析器解析到没有分号的单行代码时,它会使用一个叫做 自动分号插入算法(Automatic Semicolon Insertion) 来确定是否应该以换行符视为语句的结束,如果判断为语句结束,会在代码中断前插入一个分号到代码中。 但是,ASI 包含了一些奇怪的行为,如果 JavaScript 错误的解释了你的换行符,你的代码将会中断。 随着越来越多的新特性成为 JavaScript 的一部分,这些规则将变得更加复杂。明确地终止你的语句,并配置你的 linter 以捕获缺少分号的代码行,将有助于预防此类问题。

    // bad - 可能异常
    const jedis = {}
    ['luke', 'leia'].some((name) => {
      jedis[name] = true
      return true
    })
    
    // bad - 可能异常
    const reaction = "No! That's impossible!"
    (async function meanwhileOnTheFalcon() {
      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
      // ...
    }())
    
    // bad - 返回 `undefined` 而不是下一行的值 - 当 `return` 单独一行的时候 ASI 总是会发生
    function foo() {
      return
        'search your feelings, you know it to be foo'
    }
    
    // good
    const jedis = {};
    ['luke', 'leia'].some((name) => {
      jedis[name] = true;
      return true;
    });
    
    // good
    const reaction = "No! That's impossible!";
    (async function meanwhileOnTheFalcon() {
      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
      // ...
    }());
    
    // good
    function foo() {
      return 'search your feelings, you know it to be foo';
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
Last Updated: 11/2/2020, 2:25:14 PM