JavaScript代码风格

参考大厂爱彼迎

引用

  • 对所有的引用使用 const ;不要使用 var

    为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。

    // bad
    var a = 1
    var b = 2
    
    // good
    const a = 1
    const b = 2
    
  • 如果你一定需要可变动的引用,使用 let 代替 var

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

    // bad
    var count = 1
    if (true) {
    count += 1
    }
    
    // good, use the let.
    let count = 1
    if (true) {
    count += 1
    }
    
  • 注意 letconst 都是块级作用域。
    // const 和 let 只存在于它们被定义的区块内。
    {
    let a = 1
    const b = 1
    }
    console.log(a) // ReferenceError
    console.log(b) // ReferenceError
    

对象

  • 使用字面值创建对象。
    // bad
    const item = new Object();
    
    // good
    const item = {};
    
  • 如果你的代码在浏览器环境下执行,别使用 保留字 作为键值。这样的话在 IE8 不会运行。 更多信息。 但在 ES6 模块和服务器端中使用没有问题。
    // bad
    const superman = {
    default: { clark: 'kent' },
    private: true
    }
    
    // good
    const superman = {
    defaults: { clark: 'kent' },
    hidden: true
    }
    
  • 用同义词替换需要使用的保留字。
    // bad
    const superman = {
    class: 'alien',
    }
    
    // bad
    const superman = {
    klass: 'alien',
    }
    
    // good
    const superman = {
    type: 'alien',
    }
    
  • 使用对象方法的简写。
    // bad
    const atom = {
    value: 1,
    addValue: function (value) {
      return atom.value + value
    }
    };
    
    // good
    const atom = {
    value: 1,
    addValue(value) {
      return atom.value + value
    },
    }
    

数组

  • 使用字面值创建数组。
    // bad
    const items = new Array();
    
    // good
    const items = [];
    
  • 向数组添加元素时使用 Arrary#push 替代直接赋值。
    const someStack = []
    // bad
    someStack[someStack.length] = 'abracadabra'
    
    // good
    someStack.push('abracadabra')
    
  • 使用拓展运算符 ... 复制数组。
    // bad
    const len = items.length
    const itemsCopy = []
    let i
    
    for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i]
    }
    
    // good
    const itemsCopy = [...items]
    
  • 使用 Array#from 把一个类数组对象转换成数组。
    const foo = document.querySelectorAll('.foo')
    const nodes = Array.from(foo)
    

解构

  • 使用解构存取和使用多属性对象。

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

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

Strings

  • 字符串使用单引号 ''
    // bad
    const name = "Capt. Janeway"
    
    // good
    const name = 'Capt. Janeway'
    
  • 字符串超过 80 个字节应该使用字符串连接号换行。

  • 注:过度使用字串连接符号可能会对性能造成影响。jsPerf讨论.

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

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

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

函数

  • 使用函数声明代替函数表达式。

    为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得箭头函数可以取代函数表达式。

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

箭头函数

  • 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。

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

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

    // bad
    [1, 2, 3].map(function (x) {
    return x * x
    })
    
    // good
    [1, 2, 3].map((x) => {
    return x * x
    })
    

模块

  • 总是使用模组 (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。

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

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

注释

  • 使用 /** ... */ 作为多行注释。包含描述、指定所有参数和返回值的类型和值。
    // 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;
    }
    
  • 使用 // 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。
    // 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;
    }
    
  • 给注释增加 FIXMETODO 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

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

    class Calculator {
    constructor() {
      // FIXME: shouldn't use a global here
      total = 0;
    }
    }
    
  • 使用 // TODO: 标注问题的解决方式。
    class Calculator {
    constructor() {
      // TODO: total should be configurable by an options param
      this.total = 0;
    }
    }
    

命名规则

  • 避免单字母命名。命名应具备描述性。
    // bad
    function q() {
    // ...stuff...
    }
    
    // good
    function query() {
    // ..stuff..
    }
    
  • 使用驼峰式命名对象、函数和实例。
    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}
    
  • 使用帕斯卡式命名构造函数或类。
    // 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',
    });
    
  • 使用下划线 _ 开头命名私有属性。
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';