Skip to content

getjll/JavaScript-Style-Guide

Repository files navigation

用于本人提高代码风格、学习在写代码中的一些坑。

同时提高自己的英文文档阅读能力!!!!

强烈建议看原版 => Airbnb JavaScript Style Guide

Airbnb JavaScript 风格指南() {

更合理的书写 JavaScript

Note: this guide assumes you are using Babel, and requires that you use babel-preset-airbnb or the equivalent. It also assumes you are installing shims/polyfills in your app, with airbnb-browser-shims or the equivalent.

Downloads Downloads Gitter

其他风格指南

目录

  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. jQuery
  27. ECMAScript 5 兼容性
  28. ECMAScript 6+ (ES 2015+) 风格
  29. Standard Library
  30. 测试
  31. 性能
  32. 相关资源
  33. 谁使用了
  34. 翻译
  35. JavaScript 风格指南说明
  36. 一起来讨论 JavaScript
  37. 贡献者
  38. 许可证

类型

  • 1.1 基本类型: 使用基本类型时实际上是访问了值。

    • string
    • number
    • boolean
    • null
    • undefined
    • symbol
    const foo = 1;
    let bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
    • Symbols cannot be faithfully polyfilled, so they should not be used when targeting browsers/environments that don’t support them natively.

  • 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

    为什么? 这将确保你不能重新分配你的引用,从而导致错误和难以理解的代码.

    // 坏
    var a = 1;
    var b = 2;
    
    // 好
    const a = 1;
    const b = 2;

  • 2.2 如果必须重新赋值声明,请使用 let 代替 var。 eslint: no-var jscs: disallowVar

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

    // 坏
    var count = 1;
    if (true) {
      count += 1;
    }
    
    // 使用 let 更好.
    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

    // 坏
    const item = new Object();
    
    // 好
    const item = {};

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

    为什么? 它们允许你在一个地方定义所有属性

    function getKey(k) {
      return `a key named ${k}`;
    }
    
    // 坏
    const obj = {
      id: 5,
      name: 'San Francisco',
    };
    obj[getKey('enabled')] = true;
    
    // 好
    const obj = {
      id: 5,
      name: 'San Francisco',
      [getKey('enabled')]: true,
    };

  • 3.3 简写对象方法。 eslint: object-shorthand jscs: requireEnhancedObjectLiterals

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

  • 3.4 属性简写 eslint: object-shorthand jscs: requireEnhancedObjectLiterals

    为什么? 书写和描述简洁。

    const lukeSkywalker = 'Luke Skywalker';
    
    // 坏
    const obj = {
      lukeSkywalker: lukeSkywalker,
    };
    
    // 好
    const obj = {
      lukeSkywalker,
    };

  • 3.5 简写属性一起写在对象声明头部

    为什么? 可以清晰知道定义哪些简写属性

    const anakinSkywalker = 'Anakin Skywalker';
    const lukeSkywalker = 'Luke Skywalker';
    
    // 坏
    const obj = {
      episodeOne: 1,
      twoJediWalkIntoACantina: 2,
      lukeSkywalker,
      episodeThree: 3,
      mayTheFourth: 4,
      anakinSkywalker,
    };
    
    // 好
    const obj = {
      lukeSkywalker,
      anakinSkywalker,
      episodeOne: 1,
      twoJediWalkIntoACantina: 2,
      episodeThree: 3,
      mayTheFourth: 4,
    };

  • 3.6 非法属性才使用引号包裹 eslint: quote-props jscs: disallowQuotedKeysInObjects

    为什么? 一般来说,我们认为它主观容易阅读。它提高了语法高亮显示,也更容易地优化了许多JS引擎。

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

  • 3.7 不要直接调用 Object.prototype 方法, 例如 hasOwnProperty, propertyIsEnumerable, 和 isPrototypeOf

    为什么? 这些方法可能被对象的属性所遮蔽 - 考虑 { hasOwnProperty: false } - 或, 对象可以是空对象 (Object.create(null)).

    // 糟糕
    console.log(object.hasOwnProperty(key));
    
    // 好
    console.log(Object.prototype.hasOwnProperty.call(object, key));
    
    // 更好
    const has = Object.prototype.hasOwnProperty; // 在模块范围内缓存一次查找
    /* or */
    import has from 'has'; // https://www.npmjs.com/package/has
    // ...
    console.log(has.call(object, key));

  • 3.8 浅拷贝对象时,使用对象展开符代替Object.assign。使用对象解构操作符赋值属性。

    // 太糟糕了
    const original = { a: 1, b: 2 };
    const copy = Object.assign(original, { c: 3 }); // 因为`original`被修改 ಠ_ಠ
    delete copy.a; // 所以这里要这么干
    
    // 糟糕
    const original = { a: 1, b: 2 };
    const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
    
    // 好的
    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

    // 糟糕的
    const items = new Array();
    
    // 好的
    const items = [];

  • 4.2 使用 Array#push 而不是直接向数组追加项。

    const someStack = [];
    
    // 糟糕的
    someStack[someStack.length] = 'abracadabra';
    
    // 好的
    someStack.push('abracadabra');

  • 4.3 使用数组解构 ... 进行拷贝.

    // 糟糕的
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i += 1) {
      itemsCopy[i] = items[i];
    }
    
    // 好的
    const itemsCopy = [...items];

  • 4.4 转换类数组时, 使用 Array.from.

    const foo = document.querySelectorAll('.foo');
    
    // good
    const nodes = Array.from(foo);
    
    // best
    const nodes = [...foo];

  • 4.5 Use Array.from for converting an array-like object to an array.

    const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
    
    // bad
    const arr = Array.prototype.slice.call(arrLike);
    
    // good
    const arr = Array.from(arrLike);

  • 4.6 Use Array.from instead of spread ... for mapping over iterables, because it avoids creating an intermediate array.

    // bad
    const baz = [...foo].map(bar);
    
    // good
    const baz = Array.from(foo, bar);

  • 4.7 数组方法始终显示声明返回。 如果你的函数体项8.2一样简单,则可以省略。 eslint: array-callback-return

    // 好的
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
    
    // 好的
    [1, 2, 3].map(x => x + 1);
    
    // bad - no returned value means `acc` becomes undefined after the first iteration
    [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
      const flatten = acc.concat(item);
      acc[index] = flatten;
    });
    
    // good
    [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
      const flatten = acc.concat(item);
      acc[index] = flatten;
      return flatten;
    });
    
    // 糟糕的
    inbox.filter((msg) => {
      const { subject, author } = msg;
      if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
      } else {
        return false;
      }
    });
    
    // 好的
    inbox.filter((msg) => {
      const { subject, author } = msg;
      if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
      }
    
      return false;
    });

  • 4.8 如果数组有多行,请在打开数组后和关闭数组括号之前使用换行符。
  // 糟糕的
  const arr = [
    [0, 1], [2, 3], [4, 5],
  ];

  const objectInArray = [{
    id: 1,
  }, {
    id: 2,
  }];

  const numberInArray = [
    1, 2,
  ];

  // 好
  const arr = [[0, 1], [2, 3], [4, 5]];

  const objectInArray = [
    {
      id: 1,
    },
    {
      id: 2,
    },
  ];

  const numberInArray = [
    1,
    2,
  ];

⬆ 回到目录

解构

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

    为什么? 解构可以节省创建临时变量的性能。

    // 糟糕的
    function getFullName(user) {
      const firstName = user.firstName;
      const lastName = user.lastName;
    
      return `${firstName} ${lastName}`;
    }
    
    // 好的
    function getFullName(user) {
      const { firstName, lastName } = user;
      return `${firstName} ${lastName}`;
    }
    
    // 更好的
    function getFullName({ firstName, lastName }) {
      return `${firstName} ${lastName}`;
    }

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

    const arr = [1, 2, 3, 4];
    
    // 糟糕的
    const first = arr[0];
    const second = arr[1];
    
    // 好的
    const [first, second] = arr;

为什么? 在你添加先的属性时,不改变原有顺序。

// 糟糕的
function processInput(input) {
  // 见证奇迹的时刻
  return [left, right, top, bottom];
}

// 调用方需要考虑返回的顺序
const [left, __, top] = processInput(input);

// 好
function processInput(input) {
  // 见证奇迹的时刻
  return { left, right, top, bottom };
}

// 不需要关心顺序
const { left, top } = processInput(input);

⬆ 回到目录

字符串

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

    // 糟糕的
    const name = "Capt. Janeway";
    
    // 同样糟糕的 - 模板文字应包含插值或换行符
    const name = `Capt. Janeway`;
    
    // 好的
    const name = 'Capt. Janeway';

  • 6.2 导致字符串超过100个字符的字符串不应该使用字符串连接在多行上写入。

    为什么? 断字符串是痛苦的工作,使代码少搜索。

    // 糟糕的
    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.';
    
    // 同样糟糕的
    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.';
    
    // 好的
    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 当拼接建立字符串时,使用模板字符串而不是连接。 eslint: prefer-template template-curly-spacing jscs: requireTemplateStrings

    为什么? 模板字符串给你一个可读的,简明的语法正确的换行和字符串插值功能。

    // 糟糕的
    function sayHi(name) {
      return 'How are you, ' + name + '?';
    }
    
    // 同样糟糕的
    function sayHi(name) {
      return ['How are you, ', name, '?'].join();
    }
    
    // 糟糕的
    function sayHi(name) {
      return `How are you, ${ name }?`;
    }
    
    // 优雅的
    function sayHi(name) {
      return `How are you, ${name}?`;
    }

  • 6.4 永远不要将字符串传递给 eval() ,它是许多漏洞的根源。

  • 6.5 不要转义字符串中没必要转义的字符。 eslint: no-useless-escape

    为什么? 反斜杠破坏了可读性,因此它们只能出现在必要的时候。

    // 糟糕的
    const foo = '\'this\' \i\s \"quoted\"';
    
    // 好的
    const foo = '\'this\' is "quoted"';
    const foo = `my name is '${name}'`;

⬆ 回到目录

函数

  • 7.1 使用命名函数表达式代替函数声明。 eslint: func-style jscs: disallowFunctionDeclarations

    为什么? 函数声明被挂起,这意味着它很容易-太容易-在文件中定义函数之前引用它。这损害可读性和可维护性。如果你发现一个函数的定义是大的或足够复杂的,它会干扰理解文件的其余部分,那么也许是时候把它提取到自己的模块了!不要忘记给表达式命名--匿名函数会使得在错误调用堆栈中查找问题变得更加困难。 (Discussion)

    // 糟糕的
    function foo() {
      // ...
    }
    
    // 同样糟糕的
    const foo = function () {
      // ...
    };
    
    // 好的
    const foo = function bar() {
      // ...
    };

  • 7.2 Wrap immediately invoked function expressions in parentheses. eslint: wrap-iife jscs: requireParenthesesAroundIIFE

    为什么? 一个立即调用函数表达式是一个单独的单元包装它,和它的调用括号,在括号里,干净的表达。值得注意的是,在这个世界上,模块无处不在,你几乎从不需要IIFE。

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

  • 7.3 不要在非函数块中声明函数(ifwhile,etc)。将函数赋给变量。浏览器会允许你这样做,但不幸的是它们的解析表现不一致。 eslint: no-loop-func

  • 7.4 注意: ECMA-262 定义 block 一组语句。函数声明不是语句 查看ECMA-262的说明.

    // 糟糕的
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // 好的
    let test;
    if (currentUser) {
      test = () => {
        console.log('Yup.');
      };
    }

  • 7.5 永远不要将一个参数命名为arguments。否则这个参数的优先级将高于原有函数作用域内的arguments对象。

    // 糟糕的
    function foo(name, options, arguments) {
      // ...
    }
    
    // 好的
    function foo(name, options, args) {
      // ...
    }

  • 7.6 不要使用 arguments, 选择 rest 语法 ... 代替. eslint: prefer-rest-params

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

    // 糟糕的
    function concatenateAll() {
      const args = Array.prototype.slice.call(arguments);
      return args.join('');
    }
    
    // 好的
    function concatenateAll(...args) {
      return args.join('');
    }

  • 7.7 使用默认参数语法,而不是重写函数参数。

    // 实在有点糟糕
    function handleThings(opts) {
      // 不好! 我们不应该重写函数参数。
      // 更糟糕: 如果 opts 是 falsy 将会将参数设置为一个对象
      // 是你想要的,但它可以引入微妙的错误。
      opts = opts || {};
      // ...
    }
    
    // 同样糟糕
    function handleThings(opts) {
      if (opts === void 0) {
        opts = {};
      }
      // ...
    }
    
    // 好的
    function handleThings(opts = {}) {
      // ...
    }

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

    为什么? 它将使人困惑。

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

  • 7.9 总是将默认参数放在最后。

    // 不好的
    function handleThings(opts = {}, name) {
      // ...
    }
    
    // 好的
    function handleThings(name, opts = {}) {
      // ...
    }

  • 7.10 不要使用 Function 构造函数创建新函数。 eslint: no-new-func

    为什么? 这样创建的函数类似 eval(), 存在漏洞。

    // 糟糕的
    var add = new Function('a', 'b', 'return a + b');
    
    // 同样糟糕的
    var subtract = Function('a', 'b', 'return a - b');

  • 7.11 保持function关键字的间距。 eslint: space-before-function-paren space-before-blocks

    为什么? 良好的一致性, 当你添加或移除名字时,不需要添加额外的空格。

    // 不好的
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // 好的
    const x = function () {};
    const y = function a() {};

  • 7.12 永远不要重写参数。 eslint: no-param-reassign

    为什么? 操纵传入的参数可能会导致对调用者不必要的变量副作用。

    // 糟糕的
    function f1(obj) {
      obj.key = 1;
    }
    
    // 好的
    function f2(obj) {
      const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
    }

  • 7.13 不要重现赋值参数。 eslint: no-param-reassign

    为什么? 将参数会导致意外的行为,特别是当访问 arguments 对象。它也可以导致优化问题,特别是在V8。

    // 糟糕的
    function f1(a) {
      a = 1;
      // ...
    }
    
    function f2(a) {
      if (!a) { a = 1; }
      // ...
    }
    
    // 好的
    function f3(a) {
      const b = a || 1;
      // ...
    }
    
    function f4(a = 1) {
      // ...
    }

  • 7.14 可变参函数调用,使用拓展操作符 ...。 eslint: prefer-spread

    为什么? 更简洁, 不需要上下文,同时你不能轻易组合具有 applynew 调用。

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

  • 7.15 多参数的函数定义和调用,应该像其他多行列表一样缩进的风格:每行只包含一项,同时最后一项尾随一个逗号。

    // 糟糕的
    function foo(bar,
                 baz,
                 quux) {
      // ...
    }
    
    // 好的
    function foo(
      bar,
      baz,
      quux,
    ) {
      // ...
    }
    
    // 糟糕的
    console.log(foo,
      bar,
      baz);
    
    // 好的
    console.log(
      foo,
      bar,
      baz,
    );

⬆ 回到目录

箭头函数

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

    为什么? 因为箭头函数创建新this的执行上下文, 这通常是你想要的, 同时这是更简洁的语法。

    为什么不? 如果你有一个相当复杂的函数,你可以把这个逻辑输出到它自己的函数声明中。

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

  • 8.2 如果函数体是一个简单的表达式,省略大括号,使用隐形返回。否则,保持大括号显式 return 返回值 eslint: arrow-parens, arrow-body-style jscs: disallowParenthesesAroundArrowParam, requireShorthandArrowFunctions

    为什么? 语法糖。 多个函数链接时,可读性较好。

    // 不太好
    [1, 2, 3].map(number => {
      const nextNumber = number + 1;
      `A string containing the ${nextNumber}.`;
    });
    
    // 这样更好
    [1, 2, 3].map(number => `A string containing the ${number}.`);
    
    // 同样好的
    [1, 2, 3].map((number) => {
      const nextNumber = number + 1;
      return `A string containing the ${nextNumber}.`;
    });
    
    // 更漂亮的
    [1, 2, 3].map((number, index) => ({
      [index]: number,
    }));
    
    // 隐形返回有一定的副作用
    function foo(callback) {
      const val = callback();
      if (val === true) {
        // 在回调返回为 true 时执行
      }
    }
    
    let bool = false;
    
    // 坏
    foo(() => bool = true);
    
    // 好
    foo(() => {
      bool = true;
    });

  • 8.3 如果表达式跨越多行,请将其包在括号中以获得更好的可读性。

    为什么? 这样你可以清晰的知道一个函数的开始和结束。

    // 糟糕的
    ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
        httpMagicObjectWithAVeryLongName,
        httpMethod,
      )
    );
    
    // 好的
    ['get', 'post', 'put'].map(httpMethod => (
      Object.prototype.hasOwnProperty.call(
        httpMagicObjectWithAVeryLongName,
        httpMethod,
      )
    ));

  • 8.4 如果函数只有一个参数,不使用括号,省略括号。 否者,为了清晰和一致性总是用圆括号包裹参数。 注意: 使用圆括号也是可以接受的,在这种情况下,使用ESLint"always" option选项或则不引入 jscs的 disallowParenthesesAroundArrowParam 选项。 eslint: arrow-parens jscs: disallowParenthesesAroundArrowParam

    为什么? 减少视觉混乱。

    // 糟糕的
    [1, 2, 3].map((x) => x * x);
    
    // 好的
    [1, 2, 3].map(x => x * x);
    
    // 好的
    [1, 2, 3].map(number => (
      `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
    ));
    
    // 不好的
    [1, 2, 3].map(x => {
      const y = x + 1;
      return x * y;
    });
    
    // 好的
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });

  • 8.5 避免与比较运算符(<=, >=)混淆箭头函数(=>)。 eslint: no-confusing-arrow

    // 糟糕
    const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
    
    // 糟糕
    const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
    
    // 好的
    const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
    
    // 好的
    const itemHeight = (item) => {
      const { height, largeSize, smallSize } = item;
      return height <= 256 ? largeSize : smallSize;
    };

⬆ 回到目录

类和构造函数

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

    为什么? class 更简洁,更容易推理。

    // 糟糕的
    function Queue(contents = []) {
      this.queue = [...contents];
    }
    Queue.prototype.pop = function () {
      const value = this.queue[0];
      this.queue.splice(0, 1);
      return value;
    };
    
    // 好的
    class Queue {
      constructor(contents = []) {
        this.queue = [...contents];
      }
      pop() {
        const value = this.queue[0];
        this.queue.splice(0, 1);
        return value;
      }
    }

  • 9.2 使用 extends 进行。

    为什么? 它是一个内置的方式来继承原型的功能而不破坏 instanceof.

    // 糟糕的
    const inherits = require('inherits');
    function PeekableQueue(contents) {
      Queue.apply(this, contents);
    }
    inherits(PeekableQueue, Queue);
    PeekableQueue.prototype.peek = function () {
      return this.queue[0];
    };
    
    // 好的
    class PeekableQueue extends Queue {
      peek() {
        return this.queue[0];
      }
    }

  • 9.3 方法可以返回 this 以帮助方法链接。

    // 不太好
    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
    
    // 好的
    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 这样写也是可以的,只要你能确保它正常运行。
    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

    // 糟糕的
    class Jedi {
      constructor() {}
    
      getName() {
        return this.name;
      }
    }
    
    // 同样糟糕
    class Rey extends Jedi {
      constructor(...args) {
        super(...args);
      }
    }
    
    // 好的
    class Rey extends Jedi {
      constructor(...args) {
        super(...args);
        this.name = 'Rey';
      }
    }

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

    为什么? 重复定义的类成员默认使用最后一个 - 有重复基本肯定是一个bug。

    // 糟糕
    class Foo {
      bar() { return 1; }
      bar() { return 2; }
    }
    
    // 好的
    class Foo {
      bar() { return 1; }
    }
    
    // 好的
    class Foo {
      bar() { return 2; }
    }

⬆ 回到目录

模块

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

    为什么? 模块是未来趋势, 让我们现在就拥抱它。

    // 不好的
    const AirbnbStyleGuide = require('./AirbnbStyleGuide');
    module.exports = AirbnbStyleGuide.es6;
    
    // 好可以
    import AirbnbStyleGuide from './AirbnbStyleGuide';
    export default AirbnbStyleGuide.es6;
    
    // 更好的方式
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.2 不要使用通配符导出。

    为什么? 这将确保你有一个默认的导出。

    // 糟糕的
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';
    
    // 好的
    import AirbnbStyleGuide from './AirbnbStyleGuide';

  • 10.3 不要直接在 export 上进行导出。

    为什么? 虽然同行更加简洁,但是有一个明确的 importexport 更加一致。

    // 这样不太好
    // filename es6.js
    export { es6 as default } from './AirbnbStyleGuide';
    
    // 这样比较好
    // filename es6.js
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.4 只在一个地方使用 import 导出。 eslint: no-duplicate-imports

    为什么? 同一模块多行导出更难维护。

    // 不太好
    import foo from 'foo';
    // … 同时导出额外的 … //
    import { named1, named2 } from 'foo';
    
    // 好
    import foo, { named1, named2 } from 'foo';
    
    // 好
    import foo, {
      named1,
      named2,
    } from 'foo';

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

    为什么? 可变一般应避免,但特别是当导出可变绑定时。虽然这种技术可能需要一些特殊的情况下,一般来说,只有常量引用可以导出。

    // 糟糕的
    let foo = 3;
    export { foo };
    
    // 好的
    const foo = 3;
    export { foo };

  • 10.6 在单出口的模块中,默认出口多于指定的出口。 eslint: import/prefer-default-export

    Why? To encourage more files that only ever export one thing, which is better for readability and maintainability.

    // 不太好
    export function foo() {}
    
    // 这样可以
    export default function foo() {}

  • 10.7将所有的 import 定义在头部。 eslint: import/first

    为什么? 保持他们在顶部防止令人惊讶的行为。

    // 不太好
    import foo from 'foo';
    foo.init();
    
    import bar from 'bar';
    
    // 好的
    import foo from 'foo';
    import bar from 'bar';
    
    foo.init();

  • 10.8 多个出口应该像数组和对象一样换行缩进。

    为什么? 在样式指南中,滚动括号和其他滚动括号一样遵循缩进规则,后面的逗号也一样

    // 不好的
    import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
    
    // 好的
    import {
      longNameA,
      longNameB,
      longNameC,
      longNameD,
      longNameE,
    } from 'path';

  • 10.9 禁止模块import声明中使用Webpack loader语法。 eslint: import/no-webpack-loader-syntax

    为什么? 由于Webpack语法是模块构建工具的语法糖,偏向于在webpack.config.js中使用。

    // 糟糕
    import fooSass from 'css!sass!foo.scss';
    import barCss from 'style!css!bar.css';
    
    // 好的
    import fooSass from 'foo.scss';
    import barCss from 'bar.css';

⬆ 回到目录

迭代器和生成器

  • 11.1 不要使用迭代器。更喜欢JavaScript的高阶函数,而不是for-infor-of。 eslint: no-iterator no-restricted-syntax

    为什么? 强制执行我们的不可变规则。处理返回值的纯函数比副作用更容易解释。

    使用 map() / every() / filter() / find() / findIndex() / reduce() / some() / ... 遍历数组; Object.keys() / Object.values() / Object.entries() 生成数组,以便可以遍历对象。

    const numbers = [1, 2, 3, 4, 5];
    
    // 不好
    let sum = 0;
    for (let num of numbers) {
      sum += num;
    }
    sum === 15;
    
    // 好
    let sum = 0;
    numbers.forEach((num) => {
      sum += num;
    });
    sum === 15;
    
    // 更好 (使用函数功能)
    const sum = numbers.reduce((total, num) => total + num, 0);
    sum === 15;
    
    // 不好
    const increasedByOne = [];
    for (let i = 0; i < numbers.length; i++) {
      increasedByOne.push(numbers[i] + 1);
    }
    
    // 好的
    const increasedByOne = [];
    numbers.forEach((num) => {
      increasedByOne.push(num + 1);
    });
    
    // 更好 (保持它的功能)
    const increasedByOne = numbers.map(num => num + 1);

  • 11.2 目前不要使用generators。

    为什么? 不能编译为ES5。

  • 11.3 如果你必须使用 generators 或者无视我们的劝告, 请确保它们的标识间隔正确。 eslint: generator-star-spacing

    为什么? function* 是同一概念关键字的一部分 - * 不是 function 的修饰符, function* 是一个独特的结构,不同于 function

    // 糟糕
    function * foo() {
      // ...
    }
    
    // 糟糕
    const bar = function * () {
      // ...
    };
    
    // 糟糕
    const baz = function *() {
      // ...
    };
    
    // 糟糕
    const quux = function*() {
      // ...
    };
    
    // 糟糕
    function*foo() {
      // ...
    }
    
    // 糟糕
    function *foo() {
      // ...
    }
    
    // 非常糟糕
    function
    *
    foo() {
      // ...
    }
    
    // 非常糟糕
    const wat = function
    *
    () {
      // ...
    };
    
    // 好的
    function* foo() {
      // ...
    }
    
    // 好
    const foo = function* () {
      // ...
    };

⬆ 回到目录

属性

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

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

  • 12.2 访问属性是一个变量时使用括号 []
    const luke = {
      jedi: true,
      age: 28,
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    const isJedi = getProp('jedi');

  • 12.3 Use exponentiation operator ** when calculating exponentiations. eslint: no-restricted-properties.

    // bad
    const binary = Math.pow(2, 10);
    
    // good
    const binary = 2 ** 10;

⬆ 回到目录

变量

  • 13.1 总是使用 constlet 定义变量。 不这样做会导致全局变量。 我们要避免污染全局命名空间。 Captain Planet warned us of that. eslint: no-undef prefer-const

    // 糟糕
    superPower = new SuperPower();
    
    // 好的
    const superPower = new SuperPower();

  • 13.2 使用 constlet 定义每一个变量。 eslint: one-var jscs: disallowMultipleVarDecl

    为什么? 这样可以更容易定义一个新变量, 同时你永远不用担心切换;还是,。 还可以使用调试器单步执行每个声明,而不是跳过所有的声明。

    // 糟糕
    const items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
    // 糟糕
    // (compare to above, and try to spot the mistake)
    const items = getItems(),
        goSportsTeam = true;
        dragonball = 'z';
    
    // 好的
    const items = getItems();
    const goSportsTeam = true;
    const dragonball = 'z';

  • 13.3 全部const一组,全部let一组。

    为什么? 当您稍后需要根据先前分配的变量分配一个变量时,这将很有帮助。

    // 糟糕
    let i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // 不好
    let i;
    const items = getItems();
    let dragonball;
    const goSportsTeam = true;
    let len;
    
    // 好的
    const goSportsTeam = true;
    const items = getItems();
    let dragonball;
    let i;
    let length;

  • 13.4 就近定义相关的变量

    为什么? letconst 是块级作用域而不是函数作用域。

    // 糟糕 - 不必要的函数调用
    function checkName(hasName) {
      const name = getName();
    
      if (hasName === 'test') {
        return false;
      }
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }
    
    // 好的
    function checkName(hasName) {
      if (hasName === 'test') {
        return false;
      }
    
      const name = getName();
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }

  • 13.5 不要链式定义变量。 eslint: no-multi-assign

    为什么? 链式定义变量会隐性创造全局变量。

    // 不好的
    (function example() {
      // JavaScript 是这样解析的:
      // let a = ( b = ( c = 1 ) );
      // 关键词 `let` 只作用于变量 a; 变量 b 和 c 成为了全局变量
      let a = b = c = 1;
    }());
    
    console.log(a); // throws ReferenceError
    console.log(b); // 1
    console.log(c); // 1
    
    // 好的
    (function example() {
      let a = 1;
      let b = a;
      let c = a;
    }());
    
    console.log(a); // throws ReferenceError
    console.log(b); // throws ReferenceError
    console.log(c); // throws ReferenceError
    
    // `const`也是同样的

  • 13.6 避免使用一元操作符递增和递减 (++, --)。 eslint no-plusplus

    为什么? Per the eslint documentation, 一元递增和递减陈述受自动分号的插入,导致递增或递减的值在应用沉默的错误。 更直观说明的代码含义如 num += 1,而不是 num++num--。不允许一元递增和递减的陈述也防止您递增/递减值预前无意中也可使你的程序的意外行为。

    // 糟糕
    
    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++;
      }
    }
    
    // 好的
    
    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;

  • 13.7 Avoid linebreaks before or after = in an assignment. If your assignment violates max-len, surround the value in parens. eslint operator-linebreak.

    Why? Linebreaks surrounding = can obfuscate the value of an assignment.

    // bad
    const foo =
      superLongLongLongLongLongLongLongLongFunctionName();
    
    // bad
    const foo
      = 'superLongLongLongLongLongLongLongLongString';
    
    // good
    const foo = (
      superLongLongLongLongLongLongLongLongFunctionName()
    );
    
    // good
    const foo = 'superLongLongLongLongLongLongLongLongString';

  • 13.8 Disallow unused variables. eslint: no-unused-vars

    Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.

    // bad
    
    var some_unused_var = 42;
    
    // Write-only variables are not considered as used.
    var y = 10;
    y = 5;
    
    // A read for a modification of itself is not considered as used.
    var z = 0;
    z = z + 1;
    
    // Unused function arguments.
    function getX(x, y) {
        return x;
    }
    
    // good
    
    function getXPlusY(x, y) {
      return x + y;
    }
    
    var x = 1;
    var y = a + 2;
    
    alert(getXPlusY(x, y));
    
    // 'type' is ignored even if unused because it has a rest property sibling.
    // This is a form of extracting an object that omits the specified keys.
    var { type, ...coords } = data;
    // 'coords' is now the 'data' object without its 'type' property.

⬆ back to top

变量提升

  • 14.1 var 声明会被提升至声明时作用域的顶部。constlet 有一种新的概念 暂时性死区(TDZ)所以不会。这对理解typeof 不安全很重要。
    // 我们知道这是无法工作的 (假设没有定义全局变量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');
      };
    }
    
    // 当函数名与变量名相同时也一样。
    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 计算表达式,并且总时遵循一下规则:

    • Objects 转换为 true
    • Undefined 转换为 false
    • Null 转换为 false
    • Booleans 转换为 the value of the boolean
    • Numbers 如果 +0, -0, or NaN 转换为 false,否则 true
    • Strings 空字符串''转换为 false,否则 true
    if ([0] && []) {
      // true
      // 数组 (即使是空的) 也是一个对象, 对象将被转换为 true
    }

  • 15.3 使用表达式转换, 但是明确比较字符串和数字。

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

  • 15.5 casedefault 使用花括号创建包含词法声明(例如:letconstfunction, 和 class)的代码块。

    为什么? 在整个 switch 块中词法声明是可见的,但是只在匹配 case 初始化后才赋值。 当多个 case 定义相同的声明,就会出现问题。

    // 不好
    switch (foo) {
      case 1:
        let x = 1;
        break;
      case 2:
        const y = 2;
        break;
      case 3:
        function f() {
          // ...
        }
        break;
      default:
        class C {}
    }
    
    // 好的
    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.

    // 不好
    const foo = maybe1 > maybe2
      ? "bar"
      : value1 > value2 ? "baz" : null;
    
    // 稍微好点
    const maybeNull = value1 > value2 ? 'baz' : null;
    
    // better
    const foo = maybe1 > maybe2
      ? 'bar'
      : maybeNull;
    
    // 更好的选择
    const maybeNull = value1 > value2 ? 'baz' : null;
    
    const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

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

    eslint rules: no-unneeded-ternary.

    // 不太好
    const foo = a ? a : b;
    const bar = c ? true : false;
    const baz = c ? false : true;
    
    // 好的
    const foo = a || b;
    const bar = !!c;
    const baz = !c;

  • 15.8 When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators (+, -, *, & /) since their precedence is broadly understood. eslint: no-mixed-operators

    Why? This improves readability and clarifies the developer’s intention.

    // bad
    const foo = a && b < 0 || c > 0 || d + 1 === 0;
    
    // bad
    const bar = a ** b - 5 % d;
    
    // bad
    // one may be confused into thinking (a || b) && c
    if (a || b && c) {
      return d;
    }
    
    // good
    const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
    
    // good
    const bar = (a ** b) - (5 % d);
    
    // good
    if (a || (b && c)) {
      return d;
    }
    
    // good
    const bar = a + b / c * d;

⬆ back to top

代码块

  • 16.1 在所有的多行代码块使用括号。eslint: nonblock-statement-body-position

    // 不好
    if (test)
      return false;
    
    // 好的
    if (test) return false;
    
    // 好的
    if (test) {
      return false;
    }
    
    // 坏
    function foo() { return false; }
    
    // 好
    function bar() {
      return false;
    }

  • 16.2 如果你用到多行代码块 ifesle ,请将else放在 if 闭合括号的同一行。 eslint: brace-style

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

  • 16.3 If an if block always executes a return statement, the subsequent else block is unnecessary. A return in an else if block following an if block that contains a return can be separated into multiple if blocks. eslint: no-else-return

    // bad
    function foo() {
      if (x) {
        return x;
      } else {
        return y;
      }
    }
    
    // bad
    function cats() {
      if (x) {
        return x;
      } else if (y) {
        return y;
      }
    }
    
    // bad
    function dogs() {
      if (x) {
        return x;
      } else {
        if (y) {
          return y;
        }
      }
    }
    
    // good
    function foo() {
      if (x) {
        return x;
      }
    
      return y;
    }
    
    // good
    function cats() {
      if (x) {
        return x;
      }
    
      if (y) {
        return y;
      }
    }
    
    // good
    function dogs(x) {
      if (x) {
        if (z) {
          return y;
        }
      } else {
        return z;
      }
    }

⬆ back to top

控制语句

  • 17.1 如果你的控制语句(ifwhile等。)太长或者超过单行最大长度,每个分组条件都应该单独一行。逻辑运算符是否应该开始或结束行取决于你。

    Why? Requiring operators at the beginning of the line keeps the operators aligned and follows a pattern similar to method chaining. This also improves readability by making it easier to visually follow complex logic.

    // 不好
    if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
      thing1();
    }
    
    // 不好
    if (foo === 123 &&
      bar === 'abc') {
      thing1();
    }
    
    // 不好
    if (foo === 123
      && bar === 'abc') {
      thing1();
    }
    
    // bad
    if (
      foo === 123 &&
      bar === 'abc'
    ) {
      thing1();
    }
    
    // good
    if (
      foo === 123
      && bar === 'abc'
    ) {
      thing1();
    }
    
    // 好
    if (
      (foo === 123 || bar === 'abc')
      && doesItLookGoodWhenItBecomesThatLong()
      && isThisReallyHappening()
    ) {
      thing1();
    }
    
    // good
    if (foo === 123 && bar === 'abc') {
      thing1();
    }

  • 17.2 Don't use selection operators in place of control statements.

    // bad
    !isRunning && startRunning();
    
    // good
    if (!isRunning) {
      startRunning();
    }

⬆ back to top

注释

  • 18.1 对行注释使用 /** ... */

    // 不好
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // 好的
    /**
     * make() returns a new element
     * based on the passed-in tag name
     */
    function make(tag) {
    
      // ...
    
      return element;
    }

  • 18.2 单行注释使用 // 。将单行注释放在上方。 注释行上方添加一个空白行,除非是在代码块的第一行。

    // 坏
    const active = true;  // is current tab
    
    // 好
    // is current tab
    const active = true;
    
    // 坏
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      const type = this.type || 'no type';
    
      return type;
    }
    
    // 好
    function getType() {
      console.log('fetching type...');
    
      // type的默认值为'no type'
      const type = this.type || 'no type';
    
      return type;
    }
    
    // 同样好的
    function getType() {
      // type的默认值为'no type'
      const type = this.type || 'no type';
    
      return type;
    }
  • 18.3 注释文字开始前的添加空格可以提高可读性。 eslint: spaced-comment

    // 坏
    //is current tab
    const active = true;
    
    // 好
    // is current tab
    const active = true;
    
    // 坏
    /**
     *make() returns a new element
     *based on the passed-in tag name
     */
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // 好
    /**
     * make() returns a new element
     * based on the passed-in tag name
     */
    function make(tag) {
    
      // ...
    
      return element;
    }

  • 18.4 如果你指出一个需要重新审视的问题,添加 FIXME 前缀 帮助其他的开发者快速理解。如果这一个需要解决方案的问题,添加 TODO 前缀。 这些不同于常规评论,因为它们是可操作的。 使用 FIXME: -- 需要解决 or TODO: -- 需要实现.

  • 18.5 使用 // FIXME: 注释一个问题。

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // FIXME: 不应该使用全局变量
        total = 0;
      }
    }

  • 18.6 使用 // TODO: 注释一个待解决问题。

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // TODO: total 需要变成一个可配置的选项参数
        this.total = 0;
      }
    }

⬆ 回到目录

空白

  • 19.1 使用2个空格的soft tabs (空格字符)。 eslint: indent

    // 不好的
    function foo() {
    ∙∙∙∙let name;
    }
    
    // 不好的
    function bar() {
    ∙let name;
    }
    
    // 好的
    function baz() {
    ∙∙let name;
    }

  • 19.2 在开始括号的前面添加一个空格。 eslint: space-before-blocks

    // 不好的
    function test(){
      console.log('test');
    }
    
    // 好的
    function test() {
      console.log('test');
    }
    
    // 不好
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog',
    });
    
    // 好
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog',
    });

  • 19.3 在控制语句的前括号中放置1个空格 (if, while等。)。在函数调用和声明之间没有参数列表和函数名之间的空格。 eslint: keyword-spacing

    // 不好
    if(isJedi) {
      fight ();
    }
    
    // 好
    if (isJedi) {
      fight();
    }
    
    // 不好
    function fight () {
      console.log ('Swooosh!');
    }
    
    // 好
    function fight() {
      console.log('Swooosh!');
    }

  • 19.4 运算符添加空格。 eslint: space-infix-ops

    // 不好
    const x=y+5;
    
    // 好的
    const x = y + 5;

  • 19.5 文件结尾添加一个换行符。 eslint: eol-last

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

  • 19.6在使用长方法链时使用缩进(超过2个方法链)。 点前置, 它强调该行是方法调用,而不是新语句。 eslint: newline-per-chained-call no-whitespace-before-property

    // 糟糕
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // 糟糕
    $('#items').
      find('.selected').
        highlight().
        end().
      find('.open').
        updateCount();
    
    // 好
    $('#items')
      .find('.selected')
        .highlight()
        .end()
      .find('.open')
        .updateCount();
    
    // 糟糕
    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);
    
    // 好
    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);
    
    // 好
    const leds = stage.selectAll('.led').data(data);

  • 19.7 在空白块和下一个语句之前留下空白行。

    // 不好
    if (foo) {
      return bar;
    }
    return baz;
    
    // 好
    if (foo) {
      return bar;
    }
    
    return baz;
    
    // 不好
    const obj = {
      foo() {
      },
      bar() {
      },
    };
    return obj;
    
    // 好
    const obj = {
      foo() {
      },
    
      bar() {
      },
    };
    
    return obj;
    
    // 不好
    const arr = [
      function foo() {
      },
      function bar() {
      },
    ];
    return arr;
    
    // 好
    const arr = [
      function foo() {
      },
    
      function bar() {
      },
    ];
    
    return arr;

  • 19.8 不要在代码块头部添加空白行。 eslint: padded-blocks

    // 不好
    function bar() {
    
      console.log(foo);
    
    }
    
    // 同样不好
    if (baz) {
    
      console.log(qux);
    } else {
      console.log(foo);
    
    }
    
    // bad
    class Foo {
    
      constructor(bar) {
        this.bar = bar;
      }
    }
    
    // good
    function bar() {
      console.log(foo);
    }
    
    // 好的
    if (baz) {
      console.log(qux);
    } else {
      console.log(foo);
    }

  • 19.9 在圆括号内边添加空格。 eslint: space-in-parens

    // 坏
    function bar( foo ) {
      return foo;
    }
    
    // 好
    function bar(foo) {
      return foo;
    }
    
    // 坏
    if ( foo ) {
      console.log(foo);
    }
    
    // 好
    if (foo) {
      console.log(foo);
    }

  • 19.10 在括号内边添加空格。 eslint: array-bracket-spacing

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

  • 19.11 大括号内边添加空格。eslint: object-curly-spacing

    // 坏
    const foo = {clark: 'kent'};
    
    // 好
    const foo = { clark: 'kent' };

  • 19.12 避免一行过长超过100个字符(包括空格)。 注意: 长字符串免除当前规则,不能打破字符串的规则 eslint: max-len

    为什么? 保证可读性和可维护性。

    // 坏
    const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
    
    // 坏
    $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
    
    // 好
    const foo = jsonData
      && jsonData.foo
      && jsonData.foo.bar
      && jsonData.foo.bar.baz
      && jsonData.foo.bar.baz.quux
      && jsonData.foo.bar.baz.quux.xyzzy;
    
    // 好
    $.ajax({
      method: 'POST',
      url: 'https://airbnb.com/',
      data: { name: 'John' },
    })
      .done(() => console.log('Congratulations!'))
      .fail(() => console.log('You have failed this city.'));

  • 19.13 Require consistent spacing inside an open block token and the next token on the same line. This rule also enforces consistent spacing inside a close block token and previous token on the same line. eslint: block-spacing

    // bad
    function foo() {return true;}
    if (foo) { bar = 0;}
    
    // good
    function foo() { return true; }
    if (foo) { bar = 0; }

  • 19.14 Avoid spaces before commas and require a space after commas. eslint: comma-spacing

    // bad
    var foo = 1,bar = 2;
    var arr = [1 , 2];
    
    // good
    var foo = 1, bar = 2;
    var arr = [1, 2];

  • 19.15 Enforce spacing inside of computed property brackets. eslint: computed-property-spacing

    // bad
    obj[foo ]
    obj[ 'foo']
    var x = {[ b ]: a}
    obj[foo[ bar ]]
    
    // good
    obj[foo]
    obj['foo']
    var x = { [b]: a }
    obj[foo[bar]]

  • 19.16 Avoid spaces between functions and their invocations. eslint: func-call-spacing

    // bad
    func ();
    
    func
    ();
    
    // good
    func();

  • 19.17 Enforce spacing between keys and values in object literal properties. eslint: key-spacing

    // bad
    var obj = { "foo" : 42 };
    var obj2 = { "foo":42 };
    
    // good
    var obj = { "foo": 42 };

  • 19.19 Avoid multiple empty lines and only allow one newline at the end of files. eslint: no-multiple-empty-lines

    // bad
    var x = 1;
    
    
    
    var y = 2;
    
    // good
    var x = 1;
    
    var y = 2;

⬆ back to top

逗号

  • 20.1 逗号前置? 不。 eslint: comma-style

    // 不好
    const story = [
        once
      , upon
      , aTime
    ];
    
    // 好
    const story = [
      once,
      upon,
      aTime,
    ];
    
    // 不好
    const hero = {
        firstName: 'Ada'
      , lastName: 'Lovelace'
      , birthYear: 1815
      , superPower: 'computers'
    };
    
    // 好
    const hero = {
      firstName: 'Ada',
      lastName: 'Lovelace',
      birthYear: 1815,
      superPower: 'computers',
    };

  • 20.2 最后也添加都好? 是的。 eslint: comma-dangle

    为什么? git的diff记录更清爽。 同时,你不必担心后面的逗号的问题在传统的浏览器,因为类似Babel这样的转译器将会在转译的同时删除多余的逗号。

    // 不好的 - git diff 没有尾随逗号
    const hero = {
         firstName: 'Florence',
    -    lastName: 'Nightingale'
    +    lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing']
    };
    
    // 好 - git diff 有尾随逗号
    const hero = {
         firstName: 'Florence',
         lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing'],
    };
    // 不好
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully'
    };
    
    const heroes = [
      'Batman',
      'Superman'
    ];
    
    // 好
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully',
    };
    
    const heroes = [
      'Batman',
      'Superman',
    ];
    
    // 不好
    function createHero(
      firstName,
      lastName,
      inventorOf
    ) {
      // does nothing
    }
    
    // 好
    function createHero(
      firstName,
      lastName,
      inventorOf,
    ) {
      // does nothing
    }
    
    // 好 (注意:在展开对象后面不能出现逗号)
    function createHero(
      firstName,
      lastName,
      inventorOf,
      ...heroArgs
    ) {
      // does nothing
    }
    
    // 不好
    createHero(
      firstName,
      lastName,
      inventorOf
    );
    
    // 好
    createHero(
      firstName,
      lastName,
      inventorOf,
    );
    
    // 好 (注意:在展开对象后面不能出现逗号)
    createHero(
      firstName,
      lastName,
      inventorOf,
      ...heroArgs
    );

⬆ 回到目录

分号

  • 21.1 需要。 eslint: semi

    Why? When JavaScript encounters a line break without a semicolon, it uses a set of rules called Automatic Semicolon Insertion to determine whether or not it should regard that line break as the end of a statement, and (as the name implies) place a semicolon into your code before the line break if it thinks so. ASI contains a few eccentric behaviors, though, and your code will break if JavaScript misinterprets your line break. These rules will become more complicated as new features become a part of JavaScript. Explicitly terminating your statements and configuring your linter to catch missing semicolons will help prevent you from encountering issues.

    // bad - raises exception
    const luke = {}
    const leia = {}
    [luke, leia].forEach(jedi => jedi.father = 'vader')
    
    // bad - raises exception
    const reaction = "No! That’s impossible!"
    (async function meanwhileOnTheFalcon() {
      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
      // ...
    }())
    
    // bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
    function foo() {
      return
        'search your feelings, you know it to be foo'
    }
    
    // good
    const luke = {};
    const leia = {};
    [luke, leia].forEach((jedi) => {
      jedi.father = 'vader';
    });
    
    // good
    const reaction = "No! That’s impossible!";
    (async function meanwhileOnTheFalcon() {
      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
      // ...
    }());
    
    // good
    function foo() {
      return 'search your feelings, you know it to be foo';
    }

    查看更多.

⬆ 回到目录

类型转换

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

  • 22.2 字符类型: eslint: no-new-wrappers

    // => this.reviewScore = 9;
    
    // bad
    const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
    
    // bad
    const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
    
    // 不好
    const totalScore = this.reviewScore.toString(); // 不一定返回字符串
    
    // 好
    const totalScore = String(this.reviewScore);

  • 22.3 数字类型: 使用 Number 和 总是带上转换基数的 parseInt。eslint: radix no-new-wrappers

    const inputValue = '4';
    
    // 不好
    const val = new Number(inputValue);
    
    // 不好
    const val = +inputValue;
    
    // 不好
    const val = inputValue >> 0;
    
    // 不好
    const val = parseInt(inputValue);
    
    // 好
    const val = Number(inputValue);
    
    // 好
    const val = parseInt(inputValue, 10);

  • 22.4 如果因为 parseInt性能问题不能满足你的需求,请留下注释说明原因。

    // 好
    /**
     * parseInt was the reason my code was slow.
     * Bitshifting the String to coerce it to a
     * Number made it a lot faster.
     */
    const val = inputValue >> 0;

  • 22.5 Note: 小心位操作。数字会被当成 64-bit values, 但是位操作运算符总是返回 32 位的整数 (source)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。 Discussion. 最大的32位整数是 2,147,483,647:

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

  • 22.6 布尔类型:eslint: no-new-wrappers

    const age = 0;
    
    // 不好
    const hasAge = new Boolean(age);
    
    // 好
    const hasAge = Boolean(age);
    
    // 更好
    const hasAge = !!age;

⬆ 回到目录

命名规则

  • 23.1 避免单字母名。描述你的命名。eslint: id-length

    // 不好
    function q() {
      // ...
    }
    
    // 好
    function query() {
      // ...
    }

  • 23.2 使用驼峰命名法命名对象、函数、实例。 eslint: camelcase

    // 不好
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // 好的
    const thisIsMyObject = {};
    function thisIsMyFunction() {}

  • 23.3 命名构造函数或类只使用帕斯卡拼写法。 eslint: new-cap

    // 坏
    function user(options) {
      this.name = options.name;
    }
    
    const bad = new user({
      name: 'nope',
    });
    
    // 好
    class User {
      constructor(options) {
        this.name = options.name;
      }
    }
    
    const good = new User({
      name: 'yup',
    });

  • 23.4 前后不适用下划线。 eslint: no-underscore-dangle

    为什么? 因为JavaScript中对于属性或方法没有私有的概念,尽管大家约定下划线表示“私有”,但实际上,JS中这些属性完全是对外公开的。依照这种传统的惯例,会误导开发者认为对这些做改变并不会破坏对外API的约定,或者不需要对改变进行测试。简单来说: 如果想进行私有化,必须要保证外部不可访问。

    // 不好
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // 好
    this.firstName = 'Panda';
    
    // good, in environments where WeakMaps are available
    // see https://kangax.github.io/compat-table/es6/#test-WeakMap
    const firstNames = new WeakMap();
    firstNames.set(this, 'Panda');

  • 23.5 不用缓存 this。使用箭头函数或Function#bind

    // 坏
    function foo() {
      const self = this;
      return function () {
        console.log(self);
      };
    }
    
    // 坏
    function foo() {
      const that = this;
      return function () {
        console.log(that);
      };
    }
    
    // 好
    function foo() {
      return () => {
        console.log(this);
      };
    }

  • 23.6 文件名应与其默认导出名称完全匹配。

    // 文件 1 内容
    class CheckBox {
      // ...
    }
    export default CheckBox;
    
    // 文件 2 内容
    export default function fortyTwo() { return 42; }
    
    // 文件 3 内容
    export default function insideDirectory() {}
    
    // 在其他文件中
    // 坏
    import CheckBox from './checkBox'; // 帕斯卡命名法(PascalCase) 导入/导出, 驼峰(camelCase)文件名
    import FortyTwo from './FortyTwo'; // 帕斯卡命名法 导入/定义文件名, 驼峰导出
    import InsideDirectory from './InsideDirectory'; // 帕斯卡命名法 导入/定义文件名, 驼峰导出
    
    // 坏
    import CheckBox from './check_box'; // 帕斯卡命名法 导入/导出, 蛇形命名法(snake_case)定义文件名
    import forty_two from './forty_two'; // 蛇形命名法 导入/定义文件名, 驼峰导出
    import inside_directory from './inside_directory'; // 蛇形命名法 导入,驼峰导出
    import index from './inside_directory/index'; // 记载index文件是确定的
    import insideDirectory from './insideDirectory/index'; // 记载index文件是确定的
    
    // 好
    import CheckBox from './CheckBox'; // 帕斯卡命名法 导出/导入/定义文件名
    import fortyTwo from './fortyTwo'; // 驼峰 导出/导入/定义文件名
    import insideDirectory from './insideDirectory'; // 驼峰 导出/导入/定义文件夹名 名字使用隐性的"index"
    // ^ 同事支持 insideDirectory.js 和 insideDirectory/index.js

  • 23.7 模块默认导出函数时使用驼峰命名法,并且模块名字相同。

    function makeStyleGuide() {
      // ...
    }
    
    export default makeStyleGuide;

  • 23.8 模块导出a constructor / class / singleton / function library / bare object使用帕斯卡拼写法。

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

  • 23.9 缩略词应全部大写或小写。

    为什么? 名字是为了可读性,不是为了安抚电脑算法。

    // 坏
    import SmsContainer from './containers/SmsContainer';
    
    // 坏
    const HttpRequests = [
      // ...
    ];
    
    // 好
    import SMSContainer from './containers/SMSContainer';
    
    // 好
    const HTTPRequests = [
      // ...
    ];
    
    // also good
    const httpRequests = [
      // ...
    ];
    
    // best
    import TextMessageContainer from './containers/TextMessageContainer';
    
    // best
    const requests = [
      // ...
    ];

  • 23.10 You may optionally uppercase a constant only if it (1) is exported, (2) is a const (it can not be reassigned), and (3) the programmer can trust it (and its nested properties) to never change.

    Why? This is an additional tool to assist in situations where the programmer would be unsure if a variable might ever change. UPPERCASE_VARIABLES are letting the programmer know that they can trust the variable (and its properties) not to change.

    • What about all const variables? - This is unnecessary, so uppercasing should not be used for constants within a file. It should be used for exported constants however.
    • What about exported objects? - Uppercase at the top level of export (e.g. EXPORTED_OBJECT.key) and maintain that all nested properties do not change.
    // bad
    const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';
    
    // bad
    export const THING_TO_BE_CHANGED = 'should obviously not be uppercased';
    
    // bad
    export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';
    
    // ---
    
    // allowed but does not supply semantic value
    export const apiKey = 'SOMEKEY';
    
    // better in most cases
    export const API_KEY = 'SOMEKEY';
    
    // ---
    
    // bad - unnecessarily uppercases key while adding no semantic value
    export const MAPPING = {
      KEY: 'value'
    };
    
    // good
    export const MAPPING = {
      key: 'value'
    };

⬆ back to top

访问器

  • 24.1 属性的访问器函数不是必须的。

  • 24.2 JavaScript不使用相同的getter和setter,这会引起意想不到的副作用,很难测试、维护等的原因。相反的, 如果你需要定义访问器函数,请使用 getVal() 和 setVal('hello').

    // 坏
    class Dragon {
      get age() {
        // ...
      }
    
      set age(value) {
        // ...
      }
    }
    
    // 好
    class Dragon {
      getAge() {
        // ...
      }
    
      setAge(value) {
        // ...
      }
    }

  • 24.3 如果属性/方法的结果是布尔值,使用 isVal()hasVal().

    // 坏
    if (!dragon.age()) {
      return false;
    }
    
    // 好
    if (!dragon.hasAge()) {
      return false;
    }

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

⬆ 回到目录

事件

  • 25.1 当事件需要传递额外数据时,(无论是 DOM 事件还是私有事件), 传递一个散列而不是原始值。 这允许后面的开发者添加更多参数的同时,不需要更新事件的每一个回调函数。 举个例子, 不好的:

    // 不好的
    $(this).trigger('listingUpdated', listing.id);
    
    // ...
    
    $(this).on('listingUpdated', (e, listingID) => {
      // do something with listingID
    });

    好的:

    // 好
    $(this).trigger('listingUpdated', { listingId: listing.id });
    
    // ...
    
    $(this).on('listingUpdated', (e, data) => {
      // do something with data.listingID
    });

⬆ 回到目录

jQuery

  • 26.1 jQuery对象赋值的变量 $ 开头。

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

  • 26.2 缓存 jQuery 查收。

    // 坏
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...
    
      $('.sidebar').css({
        'background-color': 'pink',
      });
    }
    
    // 好
    function setSidebar() {
      const $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...
    
      $sidebar.css({
        'background-color': 'pink',
      });
    }

  • 26.3 使用联级$('.sidebar ul')或父子选择器$('.sidebar > ul')进行DOM查找。jsPerf

  • 26.4 jQuery对象范围内使用 find 查找。

    // 坏
    $('ul', '.sidebar').hide();
    
    // 坏
    $('.sidebar').find('ul').hide();
    
    // 好
    $('.sidebar ul').hide();
    
    // 好
    $('.sidebar > ul').hide();
    
    // 好
    $sidebar.find('ul').hide();

⬆ 回到目录

ECMAScript 5 兼容性

⬆ 回到目录

ECMAScript 6+ (ES 2015+) 风格

  • 28.1 这是一个收集的各种ES6特性的链接。
  1. Arrow Functions
  2. Classes
  3. Object Shorthand
  4. Object Concise
  5. Object Computed Properties
  6. Template Strings
  7. Destructuring
  8. Default Parameters
  9. Rest
  10. Array Spreads
  11. Let and Const
  12. Exponentiation Operator
  13. Iterators and Generators
  14. Modules

  • 28.2 不要使用还未到达第三阶段的TC39 提案

    为什么? 他们还没有最终采用,他们还没有被最终采用,很可能发生改变,甚至整个被取消掉。我们要使用JavaScript语言,而不是那些提议,那些提议还没有变成语言的一部分。

Standard Library

The Standard Library contains utilities that are functionally broken but remain for legacy reasons.

  • 29.1 Use Number.isNaN instead of global isNaN. eslint: no-restricted-globals

    Why? The global isNaN coerces non-numbers to numbers, returning true for anything that coerces to NaN. If this behavior is desired, make it explicit.

    // bad
    isNaN('1.2'); // false
    isNaN('1.2.3'); // true
    
    // good
    Number.isNaN('1.2.3'); // false
    Number.isNaN(Number('1.2.3')); // true

  • 29.2 Use Number.isFinite instead of global isFinite. eslint: no-restricted-globals

    Why? The global isFinite coerces non-numbers to numbers, returning true for anything that coerces to a finite number. If this behavior is desired, make it explicit.

    // bad
    isFinite('2e3'); // true
    
    // good
    Number.isFinite('2e3'); // false
    Number.isFinite(parseInt('2e3', 10)); // true

⬆ back to top

Testing

  • 30.1 Yup.

    function foo() {
      return true;
    }

  • 30.2 不, 这会有严重的后果。:
  • 无论你使用哪个测试框架,你都应该编写测试!
  • 尽量编写为一个个小的纯函数,同时减少突变。
  • Be cautious about stubs and mocks - 你的测试会因此而变得脆弱。
  • 在Airbnb我们主要使用mochatest。偶尔会在小且分离的模块上使用tape
  • 争取100%测试覆盖率是一个很好的目标,即使它不是总能达到。
  • 每当你修复一个bug,写一个回归测试。一个没有测试的bug,迟早会重现。

⬆ 回到目录

性能

⬆ 回到目录

相关资源

学习 ES6

阅读下面的文章

工具

其他风格指南

其他风格

进阶

书籍

博客

播客

⬆ 回到目录

谁使用了

这是使用了这个风格指南的列表,发送一个拉取请求,我们会把你添加到这个列表中。

⬆ back to top

翻译

这个风格指南也有其他语言版本:

JavaScript 风格指南说明

一起来讨论 JavaScript

贡献者

许可证

(The MIT License)

Copyright (c) 2012 Airbnb

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

⬆ 回到目录

修改

我们鼓励您克隆指南后修改规则,以适应您的团队的风格指南。下面,您可以列出一些修订的风格指南。这允许您定期更新您的风格指南,而不必处理合并冲突。

};

About

Airbnb JavaScript Style Guide 中文版

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 414