ES2015 代码规范

Author Avatar
Splendour 10月 24, 2015

综述

Arrow Functions 箭头函数

  • 使用箭头函数代替匿名函数

    箭头函数使得 this 的值不会发生你不想要的改变

  // 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;
  });
  • 如果函数体只包含一个表达式,那么省略表达式的大括号并且使用隐式的 return,否则加上 return

    // good
    [1, 2, 3].map(number => `A string containing the ${number}.`);
    // bad
    [1, 2, 3].map(number => {
      const nextNumber = number + 1;
      `A string containing the ${nextNumber}.`;
    });
    // good
    [1, 2, 3].map(number => {
      const nextNumber = number + 1;
      return `A string containing the ${nextNumber}.`;
    });
    
  • 如果表达式需要多行显示,则用小括号包起来

    // 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.'
    ));
    
  • 如果函数只有一个变量,则省略小括号

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

Constructors 构造函数

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

    // 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;
      }
    }
    
  • 使用 extends 来继承

    // 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];
      }
    }
    
  • 方法可以返回 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);
    
  • 自定义 toString() 方法,可以返回自己想要的结果

    class Jedi {
    constructor(options = {}) {
      this.name = options.name || 'no name';
    }
    getName() {
      return this.name;
    }
    toString() {
      return `Jedi - ${this.getName()}`;
    }
    }
    

对象方法缩写

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

属性和值一样时,可以缩写

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

支持动态属性名

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

模板字符串

  // bad
  function sayHi(name) {
    return 'How are you, ' + name + '?';
  }
  // bad
  function sayHi(name) {
    return ['How are you, ', name, '?'].join();
  }
  // good
  function sayHi(name) {
    return `How are you, ${name}?`;
  }

Destructuring 解构

  • 使用一个对象的多个属性时,使用解构来获取

    // 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}`;
    }
    
  • 使用数组解构

    const arr = [1, 2, 3, 4];
    // bad
    const first = arr[0];
    const second = arr[1];
    // good
    const [first, second] = arr;
    
  • 多值返回时使用对象解构而不是数组解构

    这样可以随时添加新的属性以及修改属性的顺序,不需要做其他的改动

  // bad
  function processInput(input) {
    // then a miracle occurs
    return [left, right, top, bottom];
  }
  // the caller needs to think about the order of return data
  const [left, __, top] = processInput(input);
  // good
  function processInput(input) {
    // then a miracle occurs
    return { left, right, top, bottom };
  }
  // the caller selects only the data they need
  const { left, right } = processInput(input);

Default Parameters 参数默认值

  • 使用参数默认值,而不是去改变参数

    // really bad
    function handleThings(opts) {
    // No! We shouldn't mutate function arguments.
    // Double bad: if opts is falsy it'll be set to an object which may
    // be what you want but it can introduce subtle bugs.
    opts = opts || {};
    // ...
    }
    // still bad
    function handleThings(opts) {
    if (opts === void 0) {
      opts = {};
    }
    // ...
    }
    // good
    function handleThings(opts = {}) {
    // ...
    }
    
  • 避免默认参数带来的副作用

    var b = 1;
    // bad
    function count(a = b++) {
    console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3
    
  • 总是把默认参数放在最后

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

rest

  • 使用 ... 替代 arguments

    rest 参数是一个真正的数组,而 arguments 是一个类数组

  // bad
  function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
  }
  // good
  function concatenateAll(...args) {
    return args.join('');
  }
  • 使用 ... 来复制数组
    // bad
    const len = items.length;
    const itemsCopy = [];
    let i;
    for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
    }
    // good
    const itemsCopy = [...items];
    

constlet

  • 使用 const 来定义所有的引用

    const 保证你不能改变你的引用,从而导致未知的 bug

  // bad
  var a = 1;
  var b = 2;
  // good
  const a = 1;
  const b = 2;
  • 使用 let 定义变量

    // bad
    var count = 1;
    if (true) {
      count += 1;
    }
    // good, use the let.
    let count = 1;
    if (true) {
      count += 1;
    }
    
  • 变量定义 constlet 都拥有块级作用域

    // const and let only exist in the blocks they are defined in.
    {
    let a = 1;
    const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError
    

Iterators and Generators

  • 不要使用迭代器,使用 map(), reduce(), forEach() 等替代 for-of 循环语句
    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;
    

Modules 模块

  • 从现在开始,使用 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;
    
  • 不要 import 通配符

    // bad
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';
    // good
    import AirbnbStyleGuide from './AirbnbStyleGuide';
    
  • 不要直接从 export 一个 import 表达式

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