Skip to content

yihouzenmeban/javascript

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

用更合理的方式写 JavaScript

翻译自 Airbnb JavaScript Style Guide 。 根据咱们自己项目所用的规则有删减和修改, 每条后面带 已删除 标签的表示经过投票已经删除。

目录

  1. 类型
  2. 引用
  3. 对象
  4. 数组
  5. 解构
  6. 字符串
  7. 函数
  8. 箭头函数
  9. 类 & 构造函数
  10. 模块
  11. Iterators & Generators
  12. 属性
  13. 变量
  14. 作用域提升
  15. 比较运算符 & 等号
  16. 代码块
  17. 注释
  18. 空格
  19. 逗号
  20. 分号
  21. 类型转换
  22. 命名规则
  23. 存取器
  24. 事件
  25. jQuery
  26. ECMAScript 5 兼容性
  27. 编辑器配置格式化

类型

  • 1.1 基本类型: 直接存取基本类型。

    • string
    • number
    • boolean
    • null
    • undefined
    const foo = 1;
    let bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9

  • 1.2 复杂类型: 通过引用的方式存取复杂类型。

    • object
    • array
    • function
    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 和难以理解的代码。

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

  • 2.2 如果你需要对引用重新赋值,使用 let 代替 var

    为什么?因为 let 是块级作用域,而 var 是函数作用域。

    // bad
    var count = 1;
    if (true) {
        count += 1;
    }
    
    // good
    let count = 1;
    if (true) {
        count += 1;
    }

  • 2.3 letconst 都是块级作用域。

    // const 和 let 只存在于它们被定义的区块内。
    {
        let a = 1;
        const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError

⬆ 返回目录

对象

  • 3.1 使用字面值创建对象。eslint: no-new-object

    // bad
    const item = new Object();
    
    // good
    const item = {};

  • 3.2 创建有动态属性名的对象时,使用可被计算的属性名称。

    为什么?因为这样可以让你一次定义所有的对象属性。

    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.3 使用对象方法的简写。eslint: object-shorthand

    // bad
    const atom = {
        value: 1,
    
        addValue: function (value) {
            return atom.value + value;
        },
    };
    
    // good
    const atom = {
        value: 1,
    
        addValue(value) {
            return atom.value + value;
        },
    };

  • 3.4 使用对象属性值的简写。eslint: object-shorthand

    为什么?因为这样更简短而且更形象。

    const lukeSkywalker = 'Luke Skywalker';
    
    // bad
    const obj = {
        lukeSkywalker: lukeSkywalker,
    };
    
    // good
    const obj = {
        lukeSkywalker,
    };

  • 3.5 声明对象属性之前对简写的属性分组。

    为什么?因为这样能清楚地看出哪些属性使用了简写。

    const anakinSkywalker = 'Anakin Skywalker';
    const lukeSkywalker = 'Luke Skywalker';
    
    // bad
    const obj = {
        episodeOne: 1,
        twoJedisWalkIntoACantina: 2,
        lukeSkywalker,
        episodeThree: 3,
        mayTheFourth: 4,
        anakinSkywalker,
    };
    
    // good
    const obj = {
        lukeSkywalker,
        anakinSkywalker,
        episodeOne: 1,
        twoJedisWalkIntoACantina: 2,
        episodeThree: 3,
        mayTheFourth: 4,
    };

  • 3.6 对无效的标识符使用引号。eslint: quote-props

    为什么? 一般我们主观上认为这样可读性更好,这样可以改善语法高亮,而且更容易被 JS 引擎优化。

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

  • 3.7 最好使用扩展运算符代替 Object.assign 去浅拷贝对象,重新定义一个对象去得到一个去除确定属性的的新对象。

    // very bad
    const original = { a: 1, b: 2 };
    const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
    delete copy.a; // so does this
    
    // bad
    const original = { a: 1, b: 2 };
    const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
    
    // good
    const original = { a: 1, b: 2 };
    const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
    
    const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

⬆ 返回目录

数组

  • 4.1 使用字面值创建数组。eslint: no-array-constructor

    // bad
    const items = new Array();
    
    // good
    const items = [];

  • 4.2 向数组添加元素时使用 Array.push 替代直接赋值。

    const someStack = [];
    
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');

  • 4.3 使用扩展运算符 ... 复制数组。

    // 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 把一个类数组对象转换成数组。

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

  • 4.5 在数组方法的回调函数中使用 return 语句。如果函数部分只有一个单行申明例如 8.2,return 也可以省略掉。 eslint: array-callback-return

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

    为什么?因为解构能减少临时引用属性。

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

  • 5.2 对数组使用解构赋值。jscs: requireArrayDestructuring

    const arr = [1, 2, 3, 4];
    
    // bad
    const first = arr[0];
    const second = arr[1];
    
    // good
    const [first, second] = arr;

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

    为什么?增加属性或者改变排序不会改变调用时的位置。

    // bad
    function processInput(input) {
        // then a miracle occurs
        return [left, right, top, bottom];
    }
    
    // 调用时需要考虑回调数据的顺序。
    const [left, __, top] = processInput(input);
    
    // good
    function processInput(input) {
        // then a miracle occurs
        return { left, right, top, bottom };
    }
    
    // 调用时只选择需要的数据
    const { left, right } = processInput(input);

⬆ 返回目录

Strings

  • 6.1 字符串使用单引号 '' 。eslint: quotes

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

  • 6.2 字符串超过 120 个字节不要使用字符串连接符写成多行。

    为什么? 拆成多行的字符串很难维护而且不易于搜索。

    // 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.3 程序化生成字符串时,使用模板字符串代替字符串连接。

    为什么?模板字符串更为简洁,更具可读性。

    // 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.4 永远不要对字符串使用 eval(),这个函数会引起很多问题。

  • 6.5 不要在字符串中使用无意义的转义 eslint: no-useless-escape

    为什么? 反斜杠使可读性变差,因此只在必要的时候使用。

    // bad
    const foo = '\'this\' \i\s \"quoted\"';
    
    // good
    const foo = '\'this\' is "quoted"';
    const foo = `my name is '${name}'`;

⬆ 返回目录

函数

  • 7.1 使用函数表达式代替函数声明。 已删除

    为什么?函数声明会把整个函数提升(hoisted),这意味着我们很容易在函数定义的位置之前去引用这个函数,会影响到代码的可维护性和可读性。

    // bad
    function foo() {
    }
    
    // good
    const foo = function () {
    };

  • 7.2 把需要立即执行的函数包裹起来(包裹 function 表达式)。eslint: wrap-iife

    // 立即调用的函数表达式 (IIFE)
    (() => {
        console.log('Welcome to the Internet. Please follow me.');
    })();

  • 7.3 永远不要在一个非函数代码块(ifwhile 等)中声明一个函数。把那个函数赋给一个变量再使用。虽然浏览器允许你这么做,但它们的解析表现不一致。eslint: no-loop-func

  • 7.4 注意: ECMA-262 把 block 定义为一组语句。函数声明不是语句。阅读 ECMA-262 关于这个问题的说明

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

  • 7.5 永远不要把参数命名为 arguments。这将覆盖原来函数作用域内的 arguments 对象。

    // bad
    function nope(name, options, arguments) {
        // ...
    }
    
    // good
    function yup(name, options, args) {
        // ...
    }

  • 7.6 不要使用 arguments。可以选择 rest 语法 ... 替代。

    为什么?使用 ... 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 arguments 是一个类数组。

    // bad
    function concatenateAll() {
        const args = Array.prototype.slice.call(arguments);
        return args.join('');
    }
    
    // good
    function concatenateAll(...args) {
        return args.join('');
    }

  • 7.7 直接给函数的参数指定默认值,不要改变函数参数。

    // really bad
    function handleThings(opts) {
        // 不!我们不应该改变函数参数。
        // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。
        // 但这样的写法会造成一些 Bugs。
        //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。)
        opts = opts || {};
        // ...
    }
    
    // still bad
    function handleThings(opts) {
        if (opts === void 0) {
            opts = {};
        }
        // ...
    }
    
    // good
    function handleThings(opts = {}) {
        // ...
    }

  • 7.8 直接给函数参数赋值时需要避免副作用。

    为什么?因为这样的写法让人感到很困惑。

    var b = 1;
    // bad
    function count(a = b++) {
        console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3

  • 7.9 把有默认值的参数放在最后面。

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

  • 7.10 永远不要使用 Function 去构造一个新的函数。eslint: no-new-func

    为什么?用这种方式去创建一个新的函数相当于对字符串使用 eval(),会引起很多问题。

    // bad
    var add = new Function('a', 'b', 'return a + b');
    
    // still bad
    var subtract = Function('a', 'b', 'return a - b');
    
    // good
    var x = function (a, b) {
        return a + b;
    };

  • 7.11 在函数的圆括号前后添加空格。 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() {};

  • 7.12 永远不要操作参数。eslint: no-param-reassign 已删除

    为什么?对函数参数中的变量进行操作可能会误导读者,导致混乱,也会改变 arguments 对象。

    // 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 引擎中。

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

  • 7.14 最好使用扩展运算符 ... 调用可变参数函数。eslint: prefer-spread

    Why? It's cleaner, you don't need to supply a context, and you can not easily compose new with apply.

    // bad
    const x = [1, 2, 3, 4, 5];
    console.log.apply(console, x);
    
    // good
    const x = [1, 2, 3, 4, 5];
    console.log(...x);
    
    // bad
    new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
    
    // good
    new Date(...[2016, 8, 5]);

  • 7.15 有多个参数的函数在调用或者定义的时候,如果要使用换行缩进,应该和其它多行列表使用一个标准:每一条语句占用一行,在最后加上一个逗号。

    // bad
    function foo(bar,
                 baz,
                 quux) {
        // ...
    }
    
    // good
    function foo(
        bar,
        baz,
        quux,
    ) {
      // ...
    }
    
    // bad
    console.log(foo,
        bar,
        baz);
    
    // good
    console.log(
        foo,
        bar,
        baz,
    );

⬆ 返回目录

箭头函数

  • 8.1 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。eslint: prefer-arrow-callback, arrow-spacing

    为什么?因为箭头函数创造了新的一个 this 执行环境(译注:参考 Arrow functions - JavaScript | MDNES6 arrow functions, syntax and lexical scoping),通常情况下都能满足你的需求,而且这样的写法更为简洁。

    为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。

    // 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 都省略掉。如果不是,那就不要省略。eslint: arrow-parens, arrow-body-style

    为什么?语法糖。在链式调用中可读性很高。

    // 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}.`);
    
    //bad
    [1, 2, 3].map(number => {
        const nextNumber = number + 1;
        return `A string containing the ${nextNumber}.`;
    });
    
    // 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 当表达式占用了多行的时候,为了更好的可读性,用括号包裹起来。

    为什么? 函数内容看起来会很清晰。

    // bad
    ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
            httpMagicObjectWithAVeryLongName,
            httpMethod,
        )
    );
    
    // good
    ['get', 'post', 'put'].map(httpMethod => (
        Object.prototype.hasOwnProperty.call(
            httpMagicObjectWithAVeryLongName,
            httpMethod,
        )
    ));

  • 8.4 避免箭头函数语法 (=>) 和一些比较操作 (<=, >=) 对人造成困惑。eslint: no-confusing-arrow

    // 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 => (item.height > 256 ? item.largeSize : item.smallSize);
    
    // good
    const itemHeight = (item) => {
        const { height, largeSize, smallSize } = item;
        return height > 256 ? largeSize : smallSize;
    };

⬆ 返回目录

类 & 构造器

  • 9.1 总是使用 class。避免直接操作 prototype

    为什么? 因为 class 语法更为简洁更易读。

    // 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 继承。

    为什么?因为 extends 是一个内建的原型继承方法并且不会破坏 instanceof

    // 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 来帮助链式调用。

    // 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() 方法,但要确保它能正常运行并且不会引起副作用。

    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

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

    为什么?重复的声明类成员都会被最后一个覆盖。

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

⬆ 返回目录

模块 咱们项目目前使用的是 seajs, 不支持 import 方式引用模块

  • 10.1 总是使用 (import/export) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。

    为什么?模块就是未来,让我们开始迈向未来吧。

    // 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 不要使用通配符 import。

    为什么?这样能确保你只有一个默认 export

    // bad
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';
    
    // good
    import AirbnbStyleGuide from './AirbnbStyleGuide';

  • 10.3 不要从 import 中直接 export。

    为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。

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

  • 10.4 相同路径只使用一次 import。eslint: no-duplicate-imports

    为什么?同一个路径使用多个 import 会使代码变的难以维护。

    // 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 不要 export 可变的绑定。eslint: import/no-mutable-exports

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

  • 10.6 在一个只有一个 export 的模块中,最好用默认 export 覆盖名字 export。eslint: import/prefer-default-export

    // bad
    export function foo() {}
    
    // good
    export default function foo() {}

  • 10.7 把所有的 import 放在 no-import 声明之前。eslint: import/first

    为什么?因为 import 作用域提升,把它们放在最前面防止意外.

    // bad
    import foo from 'foo';
    foo.init();
    
    import bar from 'bar';
    
    // good
    import foo from 'foo';
    import bar from 'bar';
    
    foo.init();

  • 10.8 多行的 imports 应该和多行数组以及字面值对象一样的缩进规则。

    // bad
    import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
    
    // good
    import {
        longNameA,
        longNameB,
        longNameC,
        longNameD,
        longNameE,
    } from 'path';

  • 10.9 禁止 Webpack 的加载语法出现在模块 import 语句中。eslint: import/no-webpack-loader-syntax

    为什么?在 imports 中使用 Webpack 语法会导致最后打包的代码重复,最好在 webpack.config.js 中使用加载语法。

    // bad
    import fooSass from 'css!sass!foo.scss';
    import barCss from 'style!css!bar.css';
    
    // good
    import fooSass from 'foo.scss';
    import barCss from 'bar.css';

⬆ 返回目录

Iterators and Generators

  • 11.1 不要使用 iterators。使用高阶函数替代 for-infor-of。eslint: no-iterator no-restricted-syntax

    为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。

    使用 map() / every() / filter() / find() / findIndex() / reduce() / some() / ... iterate 数组,Object.keys() / Object.values() / Object.entries() 产生数组然后 iterate 对象。

    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;
    
    // bad
    const increasedByOne = [];
    for (let i = 0; i < numbers.length; i++) {
        increasedByOne.push(numbers[i] + 1);
    }
    
    // good
    const increasedByOne = [];
    numbers.forEach(num => increasedByOne.push(num + 1));
    
    // best (keeping it functional)
    const increasedByOne = numbers.map(num => num + 1);

  • 11.2 现在还不要使用 generators。

    为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators)

  • 11.3 如果你一定要使用 generators,不想搭理 11.2,请强制 generator 函数中 * 周围使用正确的空格。eslint: generator-star-spacing

    为什么?function* 同一个概念的关键字中的一部分,* 不是修饰 function 的,function*function 不是一个东西。

    // bad
    function * foo() {
        // ...
    }
    
    // bad
    const bar = function * () {
        // ...
    };
    
    // bad
    const baz = function *() {
        // ...
    };
    
    // bad
    const quux = function*() {
        // ...
    };
    
    // bad
    function*foo() {
        // ...
    }
    
    // bad
    function *foo() {
        // ...
    }
    
    // very bad
    function
    *
    foo() {
        // ...
    }
    
    // very bad
    const wat = function
    *
    () {
        // ...
    };
    
    // good
    function* foo() {
        // ...
    }
    
    // good
    const foo = function* () {
        // ...
    };

⬆ 返回目录

属性

  • 12.1 使用 . 来访问对象的属性。eslint: dot-notation

    const luke = {
        jedi: true,
        age: 28,
    };
    
    // bad
    const isJedi = luke['jedi'];
    
    // good
    const isJedi = luke.jedi;

  • 12.2 当通过变量访问属性时使用 []

    const luke = {
        jedi: true,
        age: 28,
    };
    
    function getProp(prop) {
        return luke[prop];
    }
    
    const isJedi = getProp('jedi');

⬆ 返回目录

变量

  • 13.1 一直使用 const 来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。地球队长已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。)eslint: no-undef prefer-const

    // bad
    superPower = new SuperPower();
    
    // good
    const superPower = new SuperPower();

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

    为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错 ;,

    // bad
    const items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
    // bad
    // (compare to above, and try to spot the mistake)
    const items = getItems(),
        goSportsTeam = true;
        dragonball = 'z';
    
    // good
    const items = getItems();
    const goSportsTeam = true;
    const dragonball = 'z';

  • 13.3 将所有的 constlet 分组

    为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。

    // 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 是块级作用域而不是函数作用域。

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

  • 13.5 不要链式定义。

    为什么? 链式定义会引起隐式的全局定义。

    // bad
    (function example() {
        // JavaScript 解释为
        // let a = ( b = ( c = 1 ) );
        // let 只作用于 a,b 和 c 变成了全局定义。
        let a = b = c = 1;
    }());
    
    console.log(a); // undefined
    console.log(b); // 1
    console.log(c); // 1
    
    // good
    (function example() {
        let a = 1;
        let b = a;
        let c = a;
    }());
    
    console.log(a); // undefined
    console.log(b); // undefined
    console.log(c); // undefined
    
    // `const` 也一样。

  • 13.6 不要使用一元操作符 (++, --)。 eslint no-plusplus 已删除

    为什么? 按照 eslint 的文档,一元操作符 ++-- 会自动添加分号,不同的空白可能会改变源代码的语义。而且还会在项目中导致一些意外。

    // bad
    
    const array = [1, 2, 3];
    let num = 1;
    num++;
    --num;
    
    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;
    
    const sum = array.reduce((a, b) => a + b, 0);
    const truthyCount = array.filter(Boolean).length;

⬆ 返回目录

Hoisting

  • 14.1 var 声明会被提升至该作用域的顶部,但它们赋值不会提升。letconst 被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ)」的概念。这对于了解为什么 type of 不再安全相当重要。

    // 我们知道这样运行不了
    // (假设 notDefined 不是全局变量)
    function example() {
        console.log(notDefined); // => throws a 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); // => throws a ReferenceError
        console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
        const declaredButNotAssigned = true;
    }

  • 14.2 匿名函数表达式的变量名会被提升,但函数内容并不会。

    function example() {
        console.log(anonymous); // => undefined
    
        anonymous(); // => TypeError anonymous is not a function
    
        var anonymous = function() {
            console.log('anonymous function expression');
        };
    }

  • 14.3 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。

    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');
        };
    }
    
    // the same is true when the function name
    // is the same as the variable name.
    function example() {
        console.log(named); // => undefined
    
        named(); // => TypeError named is not a function
    
        var named = function named() {
            console.log('named');
        }
    }

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

    function example() {
        superPower(); // => Flying
    
        function superPower() {
            console.log('Flying');
        }
    }
  • 想了解更多信息,参考 Ben CherryJavaScript Scoping & Hoisting

⬆ 返回目录

比较运算符 & 等号

  • 15.1 优先使用 ===!== 而不是 ==!=。eslint: eqeqeq 已删除

  • 15.2 条件表达式例如 if 语句通过抽象方法 ToBoolean 强制计算它们的表达式并且总是遵守下面的规则:

    • 对象 被计算为 true
    • undefined 被计算为 false
    • null 被计算为 false
    • 布尔值 被计算为 布尔的值
    • 数字 如果是 +0、-0、或 NaN 被计算为 false, 否则为 true
    • 字符串 如果是空字符串 '' 被计算为 false,否则为 true
    if ([0] && []) {
      // true
      // An array is an object, objects evaluate to true
    }

  • 15.3 使用简写,显示的字符串和数字对比除外。

    // bad
    if (isValid === true) {
        // ...
    }
    
    // good
    if (isValid) {
        // ...
    }
    
    // bad
    if (name) {
        // ...
    }
    
    // good
    if (name !== '') {
        // ...
    }
    
    // bad
    if (collection.length) {
        // ...
    }
    
    // good
    if (collection.length > 0) {
        // ...
    }

  • 15.5 使用花括号创建块作用域来包裹 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 {}
        }
    }

  • 15.6 禁止使用嵌套的三元运算符。eslint rules: no-nested-ternary

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

    // 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 使用大括号包裹所有的多行代码块。

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

  • 16.2 如果通过 ifelse 使用多行代码块,把 else 放在 if 代码块关闭括号的同一行。eslint: brace-style

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

⬆ 返回目录

注释

  • 17.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;
    }

  • 17.2 使用 // 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。

    // 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 在所有的注释内容开始之前添加一个空格使它更容易被阅读。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;
    }

  • 17.4 给注释增加 FIXMETODO 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

  • 17.5 使用 // FIXME: 标注问题。

    class Calculator {
        constructor() {
            // FIXME: shouldn't use a global here
            total = 0;
        }
    }

  • 17.6 使用 // TODO: 标注问题的解决方式。

    class Calculator {
        constructor() {
            // TODO: total should be configurable by an options param
            this.total = 0;
        }
    }

⬆ 返回目录

空白

  • 18.1 使用 4 个空格作为缩进。eslint: indent

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

  • 18.2 在花括号前放一个空格。

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

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

    // bad
    const x=y+5;
    
    // good
    const x = y + 5;

  • 18.5 在文件末尾插入一个空行。eslint: eol-last

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

  • 18.5 在使用长方法链时进行缩进(超过两个方法时)。使用前面的点 . 强调这是方法调用而不是新语句。eslint: newline-per-chained-call no-whitespace-before-property

    // 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.6 在块末和新语句前插入空行。

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

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

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

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

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

  • 18.12 单行代码不要超过 120 个字符串(空格计算在内, 这个是大家投票出来确定的数值)。 注意:above, 长字符串不遵循这条规则而且长字符串不应该被折行。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.'));

⬆ 返回目录

逗号

  • 19.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',
    };

  • 19.2 增加结尾的逗号: 需要。eslint: comma-dangle 已删除

    为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的尾逗号问题

    禁止添加结尾的逗号。

    // bad - git diff without trailing comma
    const hero = {
         firstName: 'Florence',
    -    lastName: 'Nightingale'
    +    lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', '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',
    ];
    
    // bad
    function createHero(
        firstName,
        lastName,
        inventorOf
    ) {
        // does nothing
    }
    
    // good
    function createHero(
        firstName,
        lastName,
        inventorOf,
    ) {
        // does nothing
    }
    
    // good (note that a comma must not appear after a "rest" element)
    function createHero(
        firstName,
        lastName,
        inventorOf,
        ...heroArgs
    ) {
        // does nothing
    }
    
    // bad
    createHero(
        firstName,
        lastName,
        inventorOf
    );
    
    // good
    createHero(
        firstName,
        lastName,
        inventorOf,
    );
    
    // good (note that a comma must not appear after a "rest" element)
    createHero(
        firstName,
        lastName,
        inventorOf,
        ...heroArgs
    );

⬆ 返回目录

分号

  • 20.1 使用分号 eslint: semi

    // bad
    (function() {
        const name = 'Skywalker'
        return name
    })()
    
    // good
    (() => {
        const name = 'Skywalker';
        return name;
    })();
    
    // good (防止函数在两个 IIFE 合并时被当成一个参数)
    ;(() => {
        const name = 'Skywalker';
        return name;
    })();

    Read more

⬆ 返回目录

类型转换

  • 21.1 在语句开始时执行类型转换。

  • 21.2 字符串:

    // => this.reviewScore = 9;
    
    // bad
    const totalScore = this.reviewScore + ''; // 调用 this.reviewScore.valueOf()
    
    // bad
    const totalScore = this.reviewScore.toString(); // 不能保证返回一个字符串
    
    // good
    const totalScore = String(this.reviewScore);

  • 21.3 对数字使用 NumberparseInt 转换,parseInt 要带上类型转换的基数。eslint: radix

    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 成为你所做的事的瓶颈而需要使用位操作解决性能问题时,留个注释说清楚原因和你的目的。

    // good
    /**
     * 使用 parseInt 导致我的程序变慢,
     * 改成使用位操作转换数字快多了。
     */
    const val = inputValue >> 0;

  • 21.5 注: 使用位操作运算符要小心。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(参考)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。关于这个问题的讨论。最大的 32 位整数是 2,147,483,647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647

  • 21.6 布尔:

    const age = 0;
    
    // bad
    const hasAge = new Boolean(age);
    
    // good
    const hasAge = Boolean(age);
    
    // good
    const hasAge = !!age;

⬆ 返回目录

命名规则

  • 22.1 避免单字母命名。命名应具备描述性。eslint: id-length

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

  • 22.2 使用驼峰式命名对象、函数和实例。eslint: camelcase

    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}

  • 22.3 使用帕斯卡式命名(首字母大写)构造函数或类。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',
    });

  • 22.4 不要使用下划线 _ 开头或者结尾进行命名。eslint: no-underscore-dangle 已删除

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // good
    this.firstName = 'Panda';

  • 22.5 别保存 this 的引用。使用箭头函数或 Function#bind 已删除

    // 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 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。

    // file 1 contents
    class CheckBox {
        // ...
    }
    export default CheckBox;
    
    // file 2 contents
    export default function fortyTwo() { return 42; }
    
    // file 3 contents
    export default function insideDirectory() {}
    
    // in some other file
    // bad
    import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
    import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
    import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
    
    // bad
    import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
    import forty_two from './forty_two'; // snake_case import/filename, camelCase export
    import inside_directory from './inside_directory'; // snake_case import, camelCase export
    import index from './inside_directory/index'; // requiring the index file explicitly
    import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
    
    // good
    import CheckBox from './CheckBox'; // PascalCase export/import/filename
    import fortyTwo from './fortyTwo'; // camelCase export/import/filename
    import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
    // ^ supports both insideDirectory.js and insideDirectory/index.js

  • 22.7 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。

    function makeStyleGuide() {
    }
    
    export default makeStyleGuide;

  • 22.8 当你导出单例、函数库、空对象时使用帕斯卡式命名。

    const AirbnbStyleGuide = {
        es6: {
        }
    };
    
    export default AirbnbStyleGuide;

  • 22.9 缩略词或者缩写的单词应该全部大写或者小写。

    为什么?为了更好的可读性。

    // bad
    import SmsContainer from './containers/SmsContainer';
    
    // bad
    const HttpRequests = [
        // ...
    ];
    
    // good
    import SMSContainer from './containers/SMSContainer';
    
    // good
    const HTTPRequests = [
        // ...
    ];
    
    // best
    import TextMessageContainer from './containers/TextMessageContainer';
    
    // best
    const Requests = [
        // ...
    ];

⬆ 返回目录

存取器

  • 23.1 属性的存取函数不是必须的。

  • 23.2 不要使用 JavaScript getters/setters,因为它们会引起副作用,而且会导致代码很难测试,维护和阅读。如果创建获取函数,使用 getVal() 和 setVal('hello')。

    // bad
    class Dragon {
        get age() {
            // ...
        }
    
        set age(value) {
            // ...
        }
    }
    
    // good
    class Dragon {
        getAge() {
            // ...
        }
    
        setAge(value) {
            // ...
        }
    }

  • 23.3 如果属性是布尔值,使用 isVal()hasVal()

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

  • 23.4 创建 get()set() 函数是可以的,但要保持一致。

    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 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如:

    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
        // do something with listingId
    });

    更好的写法:

    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function(e, data) {
        // do something with data.listingId
    });

⬆ 返回目录

jQuery

  • 25.2 缓存 jQuery 查询。

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

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

⬆ 返回目录

ECMAScript 5 兼容性

⬆ 返回目录

About

Airbnb JavaScript 编码规范

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%