Airbnb JavaScript 的编码风格 原文地址: Airbnb/JavaScript

前言

翻译 Airbnb JavaScript Style Guide 主要是为了学习 JavaScript 编码风格。因为不是专业的译者,翻译不足之处请指出。

Airbnb JavaScript Style Guide

合理地编写 JavaScript

其他的风格指导

类型

  • 1.1 基本类型: 基本类型直接获取它的值

    • string
    • number
    • boolean
    • null
    • undefined
    1
    2
    3
    4
    5
    6
    const foo = 1;
    let bar = foo;

    bar = 9;

    console.log(foo, bar); // => 1, 9

  • 1.2 复杂类型: 复杂类型通过引用获取值

    • object
    • array
    • function
    1
    2
    3
    4
    5
    6
    const foo = [1, 2];
    const bar = foo;

    bar[0] = 9;

    console.log(foo[0], bar[0]); // => 9, 9

↑ 回到顶部

引用

  • 2.1 请使用 const 修饰所有引用而不是 var 。eslint: prefer-const, no-const-assign

    以此确保你不能对引用重新赋值,避免难以理解的代码和 Bug 。

    1
    2
    3
    4
    5
    6
    7
    // bad
    var a = 1;
    var b = 2;

    // good
    const a = 1;
    const b = 2;

  • 2.2 如果非要对引用重新复制,请使用 let 而不是 var 。eslint: no-var jscs: disallowVar

    原因是 let 为块级作用域,而 var 是 函数作用域。

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

    // good, use the let.
    let count = 1;
    if (true) {
    count += 1;
    }

  • 2.3 注意 letconst 都是块级作用域。

    1
    2
    3
    4
    5
    6
    7
    // const 和 let 只存在其定义的区块中。
    {
    let a = 1;
    const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError

↑ 回到顶部

对象

  • 3.1 请使用 literal syntax 创建对象. eslint: no-new-object

    1
    2
    3
    4
    5
    // bad
    const item = new Object();

    // good
    const item = {};

  • 3.2 如果代码在浏览器的脚本环境中执行,请勿使用[保留字]当键值,IE8中会失效。详情请看。在服务器代码和 ES6 模块中使用没问题。 jscs: disallowIdentifierNames

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    const superman = {
    default: { clark: 'kent' },
    private: true,
    };

    // good
    const superman = {
    defaults: { clark: 'kent' }, //default 多了个 s
    hidden: true,
    };

  • 3.3 请使用易懂的符号代替保留字。 jscs: disallowIdentifierNames

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    const superman = {
    class: 'alien',
    };

    // bad
    const superman = {
    klass: 'alien',
    };

    // good
    const superman = {
    type: 'alien',
    };

  • 3.4 请在创建带有动态属性名的对象时使用可计算的属性名。

    因为这样允许一次在一个位置定义对象的所有属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function getKey(k) {
    return `a key named ${k}`;
    }

    // bad
    const obj = {
    id: 5,
    name: 'San Francisco',
    };
    obj[getKey('enabled')] = true;

    // good
    const obj = {
    id: 5,
    name: 'San Francisco',
    [getKey('enabled')]: true,
    };

  • 3.5 请使用对象方法的简写。 eslint: object-shorthand jscs: requireEnhancedObjectLiterals

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad
    const atom = {
    value: 1,

    addValue: function (value) {
    return atom.value + value;
    },
    };

    // good
    const atom = {
    value: 1,

    addValue(value) {
    return atom.value + value;
    },
    };

  • 3.6 请使用属性的简写。 eslint: object-shorthand jscs: requireEnhancedObjectLiterals

    因为更短且更达意。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const lukeSkywalker = 'Luke Skywalker';

    // bad
    const obj = {
    lukeSkywalker: lukeSkywalker,
    };

    // good
    const obj = {
    lukeSkywalker,
    };

  • 3.7 简写属性放在对象声明的头部。

    原因是更容易区分哪些属性使用了简写。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const anakinSkywalker = 'Anakin Skywalker';
    const lukeSkywalker = 'Luke Skywalker';

    // bad
    const obj = {
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    lukeSkywalker,
    episodeThree: 3,
    mayTheFourth: 4,
    anakinSkywalker,
    };

    // good
    const obj = {
    lukeSkywalker,
    anakinSkywalker,
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    episodeThree: 3,
    mayTheFourth: 4,
    };

  • 3.8 只给非法标识符的属性加引号。 eslint: quote-props jscs: disallowQuotedKeysInObjects

    因为这样更易读,且提供语法高亮,还可以更容易地被很多 JS 引擎优化。

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

    // good
    const good = {
    foo: 3,
    bar: 4,
    'data-blah': 5,
    };

  • 3.9 请不要直接调用 Object.prototype 的方法。例如 hasOwnPropertypropertyIsEnumerabelisPrototypeOf

    因为这些方法可能被对象自身的属性覆盖。例如对象 { hasOwnProperty: false } 又或者是空对象 (Object.create(null))

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // bad
    console.log(object.hasOwnProperty(key));

    // good
    console.log(Object.prototype.hasOwnProperty.call(object, key));

    // best
    const has = Object.prototype.hasOwnProperty; // 在模块作用域中缓存查询。
    /* or */
    const has = require('has');

    console.log(has.call(object, key));

↑ 回到顶部

数组

  • 4.1 请使用 literal syntax 创建数组。 eslint: no-array-constructor

    1
    2
    3
    4
    5
    // bad
    const items = new Array();

    // good
    const items = [];

  • 4.2 请使用 Array#push 添加元素而不是直接复制。

    1
    2
    3
    4
    5
    6
    7
    const someStack = [];

    // bad
    someStack[someStack.length] = 'abracadabra';

    // good
    someStack.push('abracadabra');

  • 4.3 请使用数组扩散符 ... 拷贝数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    const len = items.length;
    const itemsCopy = [];
    let i;

    for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
    }

    // good
    const itemsCopy = [...items];

  • 4.4 转化类数组对象到数组时,请使用 Array.from

    1
    2
    const foo = document.querySelectorAll('.foo');
    const nodes = Array.from(foo);

  • 4.5 请在数组回调方法中使用 return 语句。如果函数体只包含一条语句,可以省略 return8.2. eslint: array-callback-return

    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
    // good
    [1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
    });

    // good
    [1, 2, 3].map(x => x + 1);

    // bad
    const flat = {};
    [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = flatten;
    });

    // good
    const flat = {};
    [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = flatten;
    return flatten;
    });

    // bad
    inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    } else {
    return false;
    }
    });

    // good
    inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    }

    return false;
    });

↑ 回到顶部

解构

  • 5.1 当访问和使用对象的多个属性时,请使用对象解构。 jscs: requireObjectDestructuring

    因为解构避免创建属性的临时引用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // bad
    function getFullName(user) {
    const firstName = user.firstName;
    const lastName = user.lastName;

    return `${firstName} ${lastName}`;
    }

    // good
    function getFullName(user) {
    const { firstName, lastName } = user;
    return `${firstName} ${lastName}`;
    }

    // best
    function getFullName({ firstName, lastName }) {
    return `${firstName} ${lastName}`;
    }

  • 5.2 请使用数组解构。 jscs: requireArrayDestructuring

    1
    2
    3
    4
    5
    6
    7
    8
    const arr = [1, 2, 3, 4];

    // bad
    const first = arr[0];
    const second = arr[1];

    // good
    const [first, second] = arr;

  • 5.3 使用对象解构返回多个值,而不是数组解构。 jscs: disallowArrayDestructuringReturn

    因为可以添加新属性或者改变顺序而不用修改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 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);

↑ 回到顶部

字符串

  • 6.1 请使用单引号 '' 括住字符串。 eslint: quotes jscs: validateQuoteMarks

    1
    2
    3
    4
    5
    // bad
    const name = "Capt. Janeway";

    // good
    const name = 'Capt. Janeway';

  • 6.2 超过100个字符的字符串应该以多行拼接的方式书写。

  • 6.3 注意:如果滥用,可能会影响性能。 jsPerf讨论.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 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.';

  • 6.4 动态地拼接字符串时,请使用字符串模板而不是拼接。 eslint: prefer-template template-curly-spacing jscs: requireTemplateStrings

    因为字符串模板易读,语法简洁,行数适中,且支持字符串插值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 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}?`;
    }

  • 6.5 请勿对字符串使用 eval() ,因为会引入太多漏洞。

  • 6.6 只在必要时使用转义符。 eslint: no-useless-escape

    因为斜杠损害了可读性,所以若无必要请勿使用。

    1
    2
    3
    4
    5
    6
    // bad
    const foo = '\'this\' \i\s \"quoted\"';

    // good
    const foo = '\'this\' is "quoted"';
    const foo = `'this' is "quoted"`;

↑ 回到顶部

函数

  • 7.1 请使用函数声明而不是函数表达式。 jscs: requireFunctionDeclarations

    因为函数声明是有名字的,所以在调用栈中更容易识别出来。而且整个函数声明会被提升,但是只有函数表达式的引用会被提升。这样可以使用 Arrow Functions 代替函数表达式。

    1
    2
    3
    4
    5
    6
    7
    // bad
    const foo = function () {
    };

    // good
    function foo() {
    }

  • 7.2 用括号括住立即执行函数。 eslint: wrap-iife jscs: requireParenthesesAroundIIFE

    因为用括号括住立即执行函数表达式和调用括号更利落地表达出独立单元的感觉。在模块遍地的世界里,基本上用不到 IIFE 。

    1
    2
    3
    4
    // immediately-invoked function expression (IIFE)
    (function () {
    console.log('Welcome to the Internet. Please follow me.');
    }());

  • 7.3 请勿在非函数区块(if,While 等)中声明函数。请将函数赋值给变量。浏览器虽然允许这么做,但是浏览器间的解释不一样。 eslint: no-loop-func

  • 7.4 注意: ECMA-262 定义 block 为一系列的语句。函数声明不是语句。 查看 ECMA-262 关于这个问题的记录Read.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    if (currentUser) {
    function test() {
    console.log('Nope.');
    }
    }

    // good
    let test;
    if (currentUser) {
    test = () => {
    console.log('Yup.');
    };
    }

  • 7.5 请勿将形参命名为 arguments 。这将覆盖每个函数的 arguments 对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function nope(name, options, arguments) {
    // ...stuff...
    }

    // good
    function yup(name, options, args) {
    // ...stuff...
    }

  • 7.6 请勿使用 arguments。请选用不定参数 ...。Never use arguments, opt to use rest syntax ... instead. eslint: prefer-rest-params

    因为 ... 鲜明地表达传入的参数,再加上,不定参数是货真价实的数组,而不是像 arguments那样的类数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // bad
    function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
    }

    // good
    function concatenateAll(...args) {
    return args.join('');
    }

  • 7.7 请使用默认参数,而不是改变函数参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // really bad
    function handleThings(opts) {
    // No! 不应该改变函数参数。
    // Double bad: 如果 opts 是可判定为 false 的。它会被设为你可能想要的,但这会引入潜在的 Bug。
    opts = opts || {};
    // ...
    }

    // still bad
    function handleThings(opts) {
    if (opts === void 0) {
    opts = {};
    }
    // ...
    }

    // good
    function handleThings(opts = {}) {
    // ...
    }

  • 7.8 避免默认参数的副作用。

    因为容易使人困惑,难以理解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var b = 1;
    // bad
    function count(a = b++) {
    console.log(a);
    }
    count(); // 1
    count(); // 2
    count(3); // 3
    count(); // 3

  • 7.9 默认参数始终置于最后。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function handleThings(opts = {}, name) {
    // ...
    }

    // good
    function handleThings(name, opts = {}) {
    // ...
    }

  • 7.10 请勿使用函数构造器创建新函数。

    因为这样创建函数对字符串求值类似于 eval() ,带来安全隐患。

    1
    2
    3
    4
    5
    // bad
    var add = new Function('a', 'b', 'return a + b');

    // still bad
    var subtract = Function('a', 'b', 'return a - b');

  • 7.11 请分隔函数签名。

    因为保持一致很好,而且不应该在增加或者移除函数名字时增加或移除空格

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

    // good
    const x = function () {};
    const y = function a() {};

  • 7.12 请勿修改形参。eslint: no-param-reassign

    因为操纵通过参数传递的对象可能给调用者造成意外的副作用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function f1(obj) {
    obj.key = 1;
    };

    // good
    function f2(obj) {
    const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
    };

  • 7.13 请勿重新赋值形参。 eslint: no-param-reassign

    原因是重新赋值或导致预料外的行为,特别是在使用 arguments 对象时。也可能造成优化问题,尤其是在 V8 中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // bad
    function f1(a) {
    a = 1;
    }

    function f2(a) {
    if (!a) { a = 1; }
    }

    // good
    function f3(a) {
    const b = a || 1;
    }

    function f4(a = 1) {
    }

↑ 回到顶部

箭头函数

  • 8.1 必须使用函数表达式时(如传递匿名函数时),请使用箭头函数。 eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions

    为什么呢?因为它创建的函数执行在当前上下文的 this 中,通常是所想要的,而且具有更准确的语义。

    为什么不呢?当函数相当复杂时,可以将逻辑移入自己的函数中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    [1, 2, 3].map(function (x) {
    const y = x + 1;
    return x * y;
    });

    // good
    [1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
    });

  • 8.2 如果函数体中包含单个表达式,请省略括号,使用隐式的 return 。否则保留括号且使用 return 语句。 eslint: arrow-parens, arrow-body-style jscs: disallowParenthesesAroundArrowParam, requireShorthandArrowFunctions

    因为这是语法糖。语法糖使得多个函数串联的时候更易读。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    [1, 2, 3].map(number => {
    const nextNumber = number + 1;
    `A string containing the ${nextNumber}.`;
    });

    // good
    [1, 2, 3].map(number => `A string containing the ${number}.`);

    // good
    [1, 2, 3].map((number) => {
    const nextNumber = number + 1;
    return `A string containing the ${nextNumber}.`;
    });

    // good
    [1, 2, 3].map((number, index) => ({
    index: number
    }));

  • 8.3 请使用括号括住跨行的表达式以便更易读。

    原因是这样鲜明地展示了函数开始和结束的位置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    [1, 2, 3].map(number => 'As time went by, the string containing the ' +
    `${number} became much longer. So we needed to break it over multiple ` +
    'lines.'
    );

    // good
    [1, 2, 3].map(number => (
    `As time went by, the string containing the ${number} became much ` +
    'longer. So we needed to break it over multiple lines.'
    ));

  • 8.4 接受单个参数的函数请省略括号。反之,必须用括号括住参数。 eslint: arrow-parens jscs: disallowParenthesesAroundArrowParam

    Why? Less visual clutter.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // bad
    [1, 2, 3].map((x) => x * x);

    // good
    [1, 2, 3].map(x => x * x);

    // good
    [1, 2, 3].map(number => (
    `A long string with the ${number}. It’s so long that we’ve broken it ` +
    'over multiple lines!'
    ));

    // bad
    [1, 2, 3].map(x => {
    const y = x + 1;
    return x * y;
    });

    // good
    [1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
    });

  • 8.5 请避免混淆箭头函数记号 (=>) 和 比较符号 (<=,>=) 。 eslint: no-confusing-arrow

    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;

    // bad
    const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;

    // good
    const itemHeight = (item) => { return item.height > 256 ? item.largeSize : item.smallSize; };

↑ 回到顶部

类 与 构造器

  • 9.1 请使用 class 避免直接操作 prototype

    因为 class 语法语义更准确且容易理解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // bad
    function Queue(contents = []) {
    this.queue = [...contents];
    }
    Queue.prototype.pop = function () {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
    };


    // good
    class Queue {
    constructor(contents = []) {
    this.queue = [...contents];
    }
    pop() {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
    }
    }

  • 9.2 请使用 extends 继承。

    因为这是不打破 instanceof ,实现继承原型功能的内建方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // bad
    const inherits = require('inherits');
    function PeekableQueue(contents) {
    Queue.apply(this, contents);
    }
    inherits(PeekableQueue, Queue);
    PeekableQueue.prototype.peek = function () {
    return this._queue[0];
    }

    // good
    class PeekableQueue extends Queue {
    peek() {
    return this._queue[0];
    }
    }

  • 9.3 方法可以返回 this 以便函数串联。

    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
    // bad
    Jedi.prototype.jump = function () {
    this.jumping = true;
    return true;
    };

    Jedi.prototype.setHeight = function (height) {
    this.height = height;
    };

    const luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20); // => undefined

    // good
    class Jedi {
    jump() {
    this.jumping = true;
    return this;
    }

    setHeight(height) {
    this.height = height;
    return this;
    }
    }

    const luke = new Jedi();

    luke.jump()
    .setHeight(20);

  • 9.4 可以写一个自定义的 toString() 方法,只需保证有用且没有副作用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Jedi {
    constructor(options = {}) {
    this.name = options.name || 'no name';
    }

    getName() {
    return this.name;
    }

    toString() {
    return `Jedi - ${this.getName()}`;
    }
    }

  • 9.5 没有声明构造函数的类拥有默认的构造函数。空的构造函数和仅仅是直接调用父类的构造函数是不必要的。eslint: no-useless-constructor

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // bad
    class Jedi {
    constructor() {}

    getName() {
    return this.name;
    }
    }

    // bad
    class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    }
    }

    // good
    class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    this.name = 'Rey';
    }
    }

  • 9.6 避免重复的类成员。 eslint: no-dupe-class-members

    因为重复的成员声明默认选择最后一个。拥有重复的成员基本上确定为 bug 。

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

    // good
    class Foo {
    bar() { return 1; }
    }

    // good
    class Foo {
    bar() { return 2; }
    }

↑ 回到顶部

模块

  • 10.1 请使用 modules (import/export) 而不是一个非标准的模块系统。这样可以随时转译到喜欢的模块系统。

    因为 Modules 是未来。现在开始步入未来吧。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    const AirbnbStyleGuide = require('./AirbnbStyleGuide');
    module.exports = AirbnbStyleGuide.es6;

    // ok
    import AirbnbStyleGuide from './AirbnbStyleGuide';
    export default AirbnbStyleGuide.es6;

    // best
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.2 请不要使用通配符导入。

    这样保证单个默认导出。

    1
    2
    3
    4
    5
    // bad
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';

    // good
    import AirbnbStyleGuide from './AirbnbStyleGuide';

  • 10.3 请不要直接在导入语句中导出。

    虽然一行更紧凑。但是清晰地导入和清晰地导出更具有一致性。

    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    // filename es6.js
    export { es6 as default } from './airbnbStyleGuide';

    // good
    // filename es6.js
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.4 只在一个位置导入。eslint: no-duplicate-imports

    从同一个路径多行导入使得代码难以维护。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // bad
    import foo from 'foo';
    // … some other imports … //
    import { named1, named2 } from 'foo';

    // good
    import foo, { named1, named2 } from 'foo';

    // good
    import foo, {
    named1,
    named2,
    } from 'foo';

  • 10.5 请不要导出可变的绑定。eslint: import/no-mutable-exports

    因为可变量应当避免通用,尤其是在导出可变的绑定时。可能在某些特殊情况下用到,但是通常情况下应该只导出常量。

    1
    2
    3
    4
    5
    6
    7
    // bad
    let foo = 3;
    export { foo }

    // good
    const foo = 3;
    export { foo }

  • 10.6 模块只有一个导出时,请使用默认导出而不是命名的导出。eslint: import/prefer-default-export

    1
    2
    3
    4
    5
    // bad
    export function foo() {}

    // good
    export default function foo() {}

  • 10.7 请将所有的 import 放到非导入语句上面。eslint: import/imports-first

    既然 import会被提升,不如将其置顶以免出现意料之外的行为。

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

    import bar from 'bar';

    // good
    import foo from 'foo';
    import bar from 'bar';

    foo.init();

↑ 回到顶部

迭代器 与 生成器

  • 11.1 请勿使用迭代器。请使用 JavaScript 的高阶函数像 map()reduce() 而不是使用循环像 for-of 。 eslint: no-iterator

    因为这样迫使遵循不可变这个规则。处理只返回值的纯函数比副作用更容易解释。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const numbers = [1, 2, 3, 4, 5];

    // bad
    let sum = 0;
    for (let num of numbers) {
    sum += num;
    }

    sum === 15;

    // good
    let sum = 0;
    numbers.forEach(num => sum += num);
    sum === 15;

    // best (use the functional force)
    const sum = numbers.reduce((total, num) => total + num, 0);
    sum === 15;

  • 11.2 目前请勿使用生成器。

    因为转义到ES5效果不好。

  • 11.3 如果非要使用生成器,或者不顾我们的建议,请确保函数签名合理地分隔。 eslint: generator-star-spacing

    因为 function* 属于同一个概念的关键字。* 不是 function 的修饰器, function* 是一个单独的构造器,与 function 不同。

    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
    // bad
    function * foo() {
    }

    const bar = function * () {
    }

    const baz = function *() {
    }

    const quux = function*() {
    }

    function*foo() {
    }

    function *foo() {
    }

    // very bad
    function
    *
    foo() {
    }

    const wat = function
    *
    () {
    }

    // good
    function* foo() {
    }

    const foo = function* () {
    }

↑ 回到顶部

属性

  • 12.1 请使用点号访问属性。eslint: dot-notation jscs: requireDotNotation

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const luke = {
    jedi: true,
    age: 28,
    };

    // bad
    const isJedi = luke['jedi'];

    // good
    const isJedi = luke.jedi;

  • 12.2 以变量访问属性时请使用方括号 []

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const luke = {
    jedi: true,
    age: 28,
    };

    function getProp(prop) {
    return luke[prop];
    }

    const isJedi = getProp('jedi');

↑ 回到顶部

变量

  • 13.1 请总是使用 const 声明变量。否则将引入全局变量。避免污染全局命名空间。地球超人(Captain Planet)已经警告过我们了。

    1
    2
    3
    4
    5
    // bad
    superPower = new SuperPower();

    // good
    const superPower = new SuperPower();

  • 13.2 每个变量请使用一个 const 声明。 eslint: one-var jscs: disallowMultipleVarDecl

    这样更容易增加新的变量声明。而且不用担心是用 , 还是 ;,带来标点符号不一致。在调试器中可以步过每一个声明,而不是一次跳过全部。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // bad
    const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

    // bad
    // (与上面相比,请试着找出错误)
    const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

    // good
    const items = getItems();
    const goSportsTeam = true;
    const dragonball = 'z';

  • 13.3 const 分为一组,然后let 分为一组。

    因为这样在重新为变量赋值时很有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // bad
    let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

    // bad
    let i;
    const items = getItems();
    let dragonball;
    const goSportsTeam = true;
    let len;

    // good
    const goSportsTeam = true;
    const items = getItems();
    let dragonball;
    let i;
    let length;

  • 13.4 请在使用变量的地方给变量赋值,但是须放置在合适的位置。

    因为 letconst 都是块级作用域而非函数级作用域。

    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
    // bad - 不必要的函数调用
    function checkName(hasName) {
    const name = getName();

    if (hasName === 'test') {
    return false;
    }

    if (name === 'test') {
    this.setName('');
    return false;
    }

    return name;
    }

    // good
    function checkName(hasName) {
    if (hasName === 'test') {
    return false;
    }

    const name = getName();

    if (name === 'test') {
    this.setName('');
    return false;
    }

    return name;
    }

↑ 回到顶部

提升

  • 14.1 var 声明会被提升至作用域顶部,相应的赋值却不会。constlet 声明被一个叫做 临时性死区 (TDZ) 的新概念所保护。所以知道为什么 typeof 不再安全 很重要。

    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
    // 没用(假设存在 notDefine 全局变量) 。
    function example() {
    console.log(notDefined); // => 抛出 ReferenceError
    }

    // 由于变量提升,在引用变量后声明变量是有效的。
    // 注意:赋值 `true` 没有被提升。
    function example() {
    console.log(declaredButNotAssigned); // => undefined
    var declaredButNotAssigned = true;
    }

    // 解释器提升变量声明至作用域顶部。
    // 也就是例子可以重写为:
    function example() {
    let declaredButNotAssigned;
    console.log(declaredButNotAssigned); // => undefined
    declaredButNotAssigned = true;
    }

    // 使用 const 和 let
    function example() {
    console.log(declaredButNotAssigned); // => 抛出 ReferenceError
    console.log(typeof declaredButNotAssigned); // => 抛出 ReferenceError
    const declaredButNotAssigned = true;
    }

  • 14.2 匿名函数的变量名会被提升,而不是函数赋值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function example() {
    console.log(anonymous); // => undefined

    anonymous(); // => TypeError anonymous is not a function

    var anonymous = function () {
    console.log('anonymous function expression');
    };
    }

  • 14.3 有名字的函数表达式的变量名会被提升,而不是函数名字或者函数体。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    superPower(); // => ReferenceError superPower is not defined

    var named = function superPower() {
    console.log('Flying');
    };
    }

    // 函数名与变量名一致时也会这样。
    function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    var named = function named() {
    console.log('named');
    }
    }

  • 14.4 函数声明中的函数名和函数体都会被提升。

    1
    2
    3
    4
    5
    6
    7
    function example() {
    superPower(); // => Flying

    function superPower() {
    console.log('Flying');
    }
    }
  • 详情请参考 JavaScript 作用域与提升 by Ben Cherry

↑ 回到顶部

比较符 与 相等性

  • 15.1 请使用 ===!== 而不是 ==!= 。 eslint: eqeqeq

  • 15.2 条件语句比如 if 语句对其表达式强制使用 ToBoolean 求值,且遵循以下几个简单的规则:

    • Objects 求值为 true
    • Undefined 求值为 false
    • Null 求值为 false
    • Booleans 求值为 相应的布尔值
    • Numbers 求值为 false 如果是 +0, -0, 或者 NaN ,否则为 true
    • String 求值为 false 如果是空字符串 '' ,否则为 true
    1
    2
    3
    4
    if ([0] && []) {
    // true
    // 数组(甚至是空数组)是对象,对象求值为 true
    }

  • 15.3 请使用快捷的方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if (name !== '') {
    // ...stuff...
    }

    // good
    if (name) {
    // ...stuff...
    }

    // bad
    if (collection.length > 0) {
    // ...stuff...
    }

    // good
    if (collection.length) {
    // ...stuff...
    }

  • 15.4 详情见 Angus Croll 的 相等的真相与 JavaScript

  • 15.5 请使用括号在包含词法声明(如 letconstfunctionclass)的 casedefault 中创建区块。

    因为词法声明在整个 switch 块中是可见的,但是只会在被赋值的时候初始化,这只会发生在相应的 case 到达时。引起多个 case 定义相同的东西的问题。

    eslint rules: no-case-declarations.

    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
    // 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 {}
    }
    }

  • 15.6 请勿嵌套三元表达式。一般情况下是单行表达式。

    eslint rules: no-nested-ternary.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // bad
    const foo = maybe1 > maybe2
    ? "bar"
    : value1 > value2 ? "baz" : null;

    // better
    const maybeNull = value1 > value2 ? 'baz' : null;

    const foo = maybe1 > maybe2
    ? 'bar'
    : maybeNull;

    // best
    const maybeNull = value1 > value2 ? 'baz' : null;

    const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

  • 15.7 请避免不需要的三元表达式。

    eslint rules: no-unneeded-ternary.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    const foo = a ? a : b;
    const bar = c ? true : false;
    const baz = c ? false : true;

    // good
    const foo = a || b;
    const bar = !!c;
    const baz = !c;

↑ 回到顶部

  • 16.1 多行区块请使用括号。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if (test)
    return false;

    // good
    if (test) return false;

    // good
    if (test) {
    return false;
    }

    // bad
    function foo() { return false; }

    // good
    function bar() {
    return false;
    }

  • 16.2 请将 else 放到离 if 最近的括号那一行。 eslint: brace-style jscs: disallowNewlineBeforeBlockStatements

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

    // good
    if (test) {
    thing1();
    thing2();
    } else {
    thing3();
    }

↑ 回到顶部

注释

  • 17.1 多行注释请使用 /** ... */。包括说明,所有参数的类型和值,以及返回值。

    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
    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {

    // ...stuff...

    return element;
    }

    // good
    /**
    * make() returns a new element
    * based on the passed in tag name
    *
    * @param {String} tag
    * @return {Element} element
    */
    function make(tag) {

    // ...stuff...

    return element;
    }

  • 17.2 单行注释请使用 //。请将单行注释至于前一行。在注释前空一行除非是第一行。

    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
    // bad
    const active = true; // is current tab

    // good
    // is current tab
    const active = true;

    // bad
    function getType() {
    console.log('fetching type...');
    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
    }

    // good
    function getType() {
    console.log('fetching type...');

    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
    }

    // also good
    function getType() {
    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
    }

  • 17.3 请在注释中使用 FIXME 或者 TODO 前缀以便其他开发者快速理解其中需要被解决的问题,或者问题解决的建议。与其他常规注释不同,这些注释可以被采取行动例如 FIXME: -- 需要指出这里 或者 TODO: -- 待实现

  • 17.4 请使用 // FIXME: 声明问题。

    1
    2
    3
    4
    5
    6
    7
    8
    class Calculator extends Abacus {
    constructor() {
    super();

    // FIXME: 此处不应使用全局变量
    total = 0;
    }
    }

  • 17.5 请使用 // TODO: 声明问题的解决方案。

    1
    2
    3
    4
    5
    6
    7
    8
    class Calculator extends Abacus {
    constructor() {
    super();

    // TODO: total 应该可由参数配置
    this.total = 0;
    }
    }

↑ 回到顶部

空白符

  • 18.1 请使用设为两个空格的 soft tabs 。 eslint: indent jscs: validateIndentation

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    function foo() {
    ∙∙∙∙const name;
    }

    // bad
    function bar() {
    const name;
    }

    // good
    function baz() {
    ∙∙const name;
    }

  • 18.2 请在第一个大括号前空一格。 eslint: space-before-blocks jscs: requireSpaceBeforeBlockStatements

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // bad
    function test(){
    console.log('test');
    }

    // good
    function test() {
    console.log('test');
    }

    // bad
    dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
    });

    // good
    dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
    });

  • 18.3 请在控制语句(ifwhile 等)的开始空格前空一格。请勿在函数名字和参数列表间留空。 eslint: keyword-spacing jscs: requireSpaceAfterKeywords

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if(isJedi) {
    fight ();
    }

    // good
    if (isJedi) {
    fight();
    }

    // bad
    function fight () {
    console.log ('Swooosh!');
    }

    // good
    function fight() {
    console.log('Swooosh!');
    }

  • 18.4 请使用空格分离操作符。 eslint: space-infix-ops jscs: requireSpaceBeforeBinaryOperators, requireSpaceAfterBinaryOperators

    1
    2
    3
    4
    5
    // bad
    const x=y+5;

    // good
    const x = y + 5;

  • 18.5 请在文件尾部以换行符结尾。

    1
    2
    3
    4
    // bad
    (function (global) {
    // ...stuff...
    })(this);
    1
    2
    3
    4
    5
    // bad
    (function (global) {
    // ...stuff...
    })(this);↵

    1
    2
    3
    4
    // good
    (function (global) {
    // ...stuff...
    })(this);↵

  • 18.6 请在长的方法链(多于两个方法)时使用缩进。请以点开头,这样强调该行为方法调用而不是新的语句。eslint: newline-per-chained-call no-whitespace-before-property

    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
    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();

    // bad
    $('#items').
    find('.selected').
    highlight().
    end().
    find('.open').
    updateCount();

    // good
    $('#items')
    .find('.selected')
    .highlight()
    .end()
    .find('.open')
    .updateCount();

    // bad
    const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

    // good
    const leds = stage.selectAll('.led')
    .data(data)
    .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
    .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

    // good
    const leds = stage.selectAll('.led').data(data);

  • 18.7 请在区块和下一个语句间空一行。 jscs: requirePaddingNewLinesAfterBlocks

    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
    51
    52
    // bad
    if (foo) {
    return bar;
    }
    return baz;

    // good
    if (foo) {
    return bar;
    }

    return baz;

    // bad
    const obj = {
    foo() {
    },
    bar() {
    },
    };
    return obj;

    // good
    const obj = {
    foo() {
    },

    bar() {
    },
    };

    return obj;

    // bad
    const arr = [
    function foo() {
    },
    function bar() {
    },
    ];
    return arr;

    // good
    const arr = [
    function foo() {
    },

    function bar() {
    },
    ];

    return arr;

  • 18.8 请勿在块中填充空行。eslint: padded-blocks jscs: disallowPaddingNewlinesInBlocks

    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
    // bad
    function bar() {

    console.log(foo);

    }

    // also bad
    if (baz) {

    console.log(qux);
    } else {
    console.log(foo);

    }

    // good
    function bar() {
    console.log(foo);
    }

    // good
    if (baz) {
    console.log(qux);
    } else {
    console.log(foo);
    }

  • 18.9 请勿在括号内加空格。eslint: space-in-parens jscs: disallowSpacesInsideParentheses

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    function bar( foo ) {
    return foo;
    }

    // good
    function bar(foo) {
    return foo;
    }

    // bad
    if ( foo ) {
    console.log(foo);
    }

    // good
    if (foo) {
    console.log(foo);
    }

  • 18.10 请勿在放括号中加空格。 eslint: array-bracket-spacing jscs: disallowSpacesInsideArrayBrackets

    1
    2
    3
    4
    5
    6
    7
    // bad
    const foo = [ 1, 2, 3 ];
    console.log(foo[ 0 ]);

    // good
    const foo = [1, 2, 3];
    console.log(foo[0]);

  • 18.11 请在花括号中加空格。eslint: object-curly-spacing jscs: requireSpacesInsideObjectBrackets

    1
    2
    3
    4
    5
    // bad
    const foo = {clark: 'kent'};

    // good
    const foo = { clark: 'kent' };

  • 18.12 请避免单行超过100个字符(含空白符)。 eslint: max-len jscs: maximumLineLength

    这样确保了可读性与可维护性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // bad
    const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';

    // 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 = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
    'Whatever wizard constrains a helpful ally. The counterpart ascends!';

    // good
    $.ajax({
    method: 'POST',
    url: 'https://airbnb.com/',
    data: { name: 'John' },
    })
    .done(() => console.log('Congratulations!'))
    .fail(() => console.log('You have failed this city.'));

↑ 回到顶部

逗号

  • 19.1 逗号开头: 别啦 eslint: comma-style jscs: requireCommaBeforeLineBreak

    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
    // 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',
    };

  • 19.2 附加的尾部逗号: 好滴 eslint: comma-dangle jscs: requireTrailingComma

    因为这样 git diffs 会更清晰。而且转译器比如 Babel 会移除尾部附加的逗号。这样不用担心在传统浏览器中的 尾部逗号问题

    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
    // bad - git diff without trailing comma
    const hero = {
    firstName: 'Florence',
    - lastName: 'Nightingale'
    + lastName: 'Nightingale',
    + inventorOf: ['coxcomb graph', 'modern nursing']
    };

    // good - git diff with trailing comma
    const hero = {
    firstName: 'Florence',
    lastName: 'Nightingale',
    + inventorOf: ['coxcomb chart', 'modern nursing'],
    };

    // bad
    const hero = {
    firstName: 'Dana',
    lastName: 'Scully'
    };

    const heroes = [
    'Batman',
    'Superman'
    ];

    // good
    const hero = {
    firstName: 'Dana',
    lastName: 'Scully',
    };

    const heroes = [
    'Batman',
    'Superman',
    ];

↑ 回到顶部

分号

  • 20.1 好滴 eslint: semi jscs: requireSemicolons

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad
    (function () {
    const name = 'Skywalker'
    return name
    })()

    // good
    (function () {
    const name = 'Skywalker';
    return name;
    }());

    // good, but legacy (guards against the function becoming an argument when two files with IIFEs are concatenated)
    ;(() => {
    const name = 'Skywalker';
    return name;
    }());

    Read more.

↑ 回到顶部

类型转换 与 强制转换

  • 21.1 语句还是前执行强制转换。

  • 21.2 Strings:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // => this.reviewScore = 9;

    // bad
    const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()

    // bad
    const totalScore = this.reviewScore.toString(); // 不保证返回 string

    // good
    const totalScore = String(this.reviewScore);

  • 21.3 Numbers: 请在类型转换时使用 Number ,在使用 parseInt 解析字符串时带上进制。 eslint: radix

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const inputValue = '4';

    // bad
    const val = new Number(inputValue);

    // bad
    const val = +inputValue;

    // bad
    const val = inputValue >> 0;

    // bad
    const val = parseInt(inputValue);

    // good
    const val = Number(inputValue);

    // good
    const val = parseInt(inputValue, 10);

  • 21.4 如果因为某种原因做了些很野性的事情,而且 parseInt 成为瓶颈,因为性能原因使用位移操作,请留注释解释原因和内容。

    1
    2
    3
    4
    5
    6
    // good
    /**
    * parseInt 是代码变慢的罪魁祸首。
    * 位移字符串强制将它转成 Number 会快很多。
    */
    const val = inputValue >> 0;

  • 21.5 注意: 请在使用位移操作小心。Number 是 64位值,而位移操作总是返回32位整数 (source)。位移可能导致整数值大于32位。 相关讨论。最大的有符号32位值为2,147,483,647:

    1
    2
    3
    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647

  • 21.6 Booleans:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const age = 0;

    // bad
    const hasAge = new Boolean(age);

    // good
    const hasAge = Boolean(age);

    // best
    const hasAge = !!age;

↑ 回到顶部

命名约定

  • 22.1 请避免使用单个字母名字。名字应具有自述性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function q() {
    // ...stuff...
    }

    // good
    function query() {
    // ..stuff..
    }

  • 22.2 请在命名对象,函数和实例时使用小驼峰式(camelCase)。eslint: camelcase jscs: requireCamelCaseOrUpperCaseIdentifiers

    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}

    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}

  • 22.3 请仅在命名构造函数和类时使用大驼峰式。eslint: new-cap jscs: requireCapitalizedConstructors

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 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',
    });

  • 22.4 请勿使用拖尾或者领头的下划线。eslint: no-underscore-dangle jscs: disallowDanglingUnderscores

    因为 JavaScript 对于属性和方法而言没有私有性。虽然领头的下划线是一种约定俗成表达 private 的方式。实际上,这些属性全是 public 而且术语公共的 API 约定。这种约定可能会导致开发者错误地以为改变不算做打破或者不需要测试。太长不看:需要 private 的东西时,不能显示地表现(给其他人)。

    1
    2
    3
    4
    5
    6
    7
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';

    // good
    this.firstName = 'Panda';

  • 22.5 请勿保存 this 的引用。请使用箭头函数或者 bind 函数。 jscs: disallowNodeTypes

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // bad
    function foo() {
    const self = this;
    return function () {
    console.log(self);
    };
    }

    // bad
    function foo() {
    const that = this;
    return function () {
    console.log(that);
    };
    }

    // good
    function foo() {
    return () => {
    console.log(this);
    };
    }

  • 22.6 文件名应当与默认导出的名字完全一致。

    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
    // 文件 1 内容
    class CheckBox {
    // ...
    }
    export default CheckBox;

    // 文件 2 内容
    export default function fortyTwo() { return 42; }

    // 文件 3 内容
    export default function insideDirectory() {}

    // 在同一个其他文件
    // bad
    import CheckBox from './checkBox'; // 大驼峰 import/export ,小驼峰文件名
    import FortyTwo from './FortyTwo'; // 大驼峰 import/filename ,小驼峰 export
    import InsideDirectory from './InsideDirectory'; // 大驼峰 import/filename ,小驼峰 export

    // bad
    import CheckBox from './check_box'; // 大驼峰 import/export ,蛇_型文件名。
    import forty_two from './forty_two'; // 蛇_型 import/export ,小驼峰 export
    import inside_directory from './inside_directory'; // 蛇_型 import ,小驼峰 export
    import index from './inside_directory/index'; // 显示地导入 index 文件
    import insideDirectory from './insideDirectory/index'; // 显示地导入 index 文件

    // good
    import CheckBox from './CheckBox'; // 大驼峰 export/import/文件名
    import fortyTwo from './fortyTwo'; // 小驼峰 export/import/文件名
    import insideDirectory from './insideDirectory'; // 小驼峰 export/import/目录名/ ,隐式的"index"
    // ^ 同时支持 insideDirectory.js 和 insideDirectory/index.js

  • 22.7 请在导出默认函数时使用小驼峰。文件名应当与函数名一致。

    1
    2
    3
    4
    function makeStyleGuide() {
    }

    export default makeStyleGuide;

  • 22.8 请在导出 构造器 / 类 / 单例 / 函数库 / 裸露的对象 时使用大驼峰。

    1
    2
    3
    4
    5
    6
    const AirbnbStyleGuide = {
    es6: {
    }
    };

    export default AirbnbStyleGuide;

↑ 回到顶部

访问器

  • 23.1 属性的访问函数是不必要的。

  • 23.2 请勿使用 JavaScript 的 getters/setters 。因为会造成副作用,难以调试,维护和理清。相应地,如果有访问函数,请使用 getVal() 和 setVal(‘hello’) 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // bad
    class Dragon {
    get age() {
    // ...
    }

    set age(value) {
    // ...
    }
    }

    // good
    class Dragon {
    getAge() {
    // ...
    }

    setAge(value) {
    // ...
    }
    }

  • 23.3 如果属性或方法是 boolean 请使用 isVal() 或者 hasVal()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    if (!dragon.age()) {
    return false;
    }

    // good
    if (!dragon.hasAge()) {
    return false;
    }

  • 23.4 可以创建 get() 和 set() 函数,但请保持一致。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Jedi {
    constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
    }

    set(key, val) {
    this[key] = val;
    }

    get(key) {
    return this[key];
    }
    }

↑ 回到顶部

事件

  • 24.1 当给事件传递数据时(无论是 DOM 事件还是 Backbone 事件),传递一个带索引的对象而不是原始值。这样随后的贡献者可以在不更新每个处理函数的情况下添加更多数据。例如,相比于:

    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    $(this).trigger('listingUpdated', listing.id);

    ...

    $(this).on('listingUpdated', (e, listingId) => {
    // do something with listingId
    });

    不如:

    1
    2
    3
    4
    5
    6
    7
    8
    // good
    $(this).trigger('listingUpdated', { listingId: listing.id });

    ...

    $(this).on('listingUpdated', (e, data) => {
    // do something with data.listingId
    });

    ↑ 回到顶部

jQuery

  • 25.1 请给 jQuery对象加上 $ 前缀。 jscs: requireDollarBeforejQueryAssignment

    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    const sidebar = $('.sidebar');

    // good
    const $sidebar = $('.sidebar');

    // good
    const $sidebarBtn = $('.sidebar-btn');

  • 25.2 缓存 jQuery 查询。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // bad
    function setSidebar() {
    $('.sidebar').hide();

    // ...stuff...

    $('.sidebar').css({
    'background-color': 'pink'
    });
    }

    // good
    function setSidebar() {
    const $sidebar = $('.sidebar');
    $sidebar.hide();

    // ...stuff...

    $sidebar.css({
    'background-color': 'pink'
    });
    }

  • 25.3 DOM查询请使用自上而下 $('.sidebar ul') 或者 父 > 子 $('.sidebar > ul')jsPerf

  • 25.4 请在 jQuery 对象中查询时使用 find

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

    // bad
    $('.sidebar').find('ul').hide();

    // good
    $('.sidebar ul').hide();

    // good
    $('.sidebar > ul').hide();

    // good
    $sidebar.find('ul').hide();

↑ 回到顶部

ECMAScript 5 兼容性

↑ 回到顶部

ECMAScript 6 风格

  • 27.1 以下是 ES6 特性的链接集合。
  1. 箭头函数
  2. 对象快捷
  3. 对象简洁
  4. 对象可计算属性
  5. 模板字符串
  6. 解构
  7. 默认参数
  8. Rest
  9. 数组扩散符
  10. Let 和 Const
  11. 迭代器和生成器
  12. 模块

↑ 回到顶部

测试

  • 28.1 好呀好呀

    1
    2
    3
    function foo() {
    return true;
    }

  • 28.2 不了,但是正经地:
    • 无论使用哪种测试框架都需要写测试用例。
    • 尽量编写许多小而纯的函数,并且最小化变动发生的地方。
    • 小心 stubs 和 mocks 。可能使得测试变得脆弱。
    • Airbnb 主要使用 mochatape 偶尔也会用来测试小的,独立的模块。
    • 100% 测试覆盖是一个很好的努力目标,尽管并不是每次都能做到。
    • 无论何时修复一个 bug ,请编写回归测试,被修复又没有回归测试的 bug 基本上肯定会在将来出现。

↑ 回到顶部

性能

↑ 回到顶部