Airbnb JavaScript 風格指南--翻譯版

Airbnb JavaScript 風格指南() {

JavaScript最合理的方法 A mostly reasonable approach to JavaScriptjavascript

注意: 這個指南假定你正在使用Babel, 而且須要你使用或等效的使用babel-preset-airbnb。 同時假定你在你的應用裏安裝了帶有或等效的airbnb-browser-shimscss

shims/polyfillshtml

目錄

  1. Types
  2. References
  3. Objects
  4. Arrays
  5. Destructuring
  6. Strings
  7. Functions
  8. Arrow Functions
  9. Classes & Constructors
  10. Modules
  11. Iterators and Generators
  12. Properties
  13. Variables
  14. Hoisting
  15. Comparison Operators & Equality
  16. Blocks
  17. Control Statements
  18. Comments
  19. Whitespace
  20. Commas
  21. Semicolons
  22. Type Casting & Coercion
  23. Naming Conventions
  24. Accessors
  25. Events
  26. jQuery
  27. ECMAScript 5 Compatibility
  28. ECMAScript 6+ (ES 2015+) Styles
  29. Standard Library
  30. Testing
  31. Performance
  32. Resources
  33. In the Wild
  34. Translation
  35. The JavaScript Style Guide Guide
  36. Chat With Us About JavaScript
  37. Contributors
  38. License
  39. Amendments

Types

  • 1.1 基本類型: 你能夠直接獲取到基本類型的值
    • string
    • number
    • boolean
    • null
    • undefined
    • symbol
    const foo = 1;
    let bar = foo;
    bar = 9;
    console.log(foo, bar); // => 1, 9
    複製代碼
    • Symbols 不能被正確的polyfill。 因此在不能原生支持symbol類型的環境[瀏覽器]中,不該該使用 symbol 類型。

  • 1.2 複雜類型: 複雜類型賦值是獲取到他的引用的值。 至關於傳引用
    • object
    • array
    • function
    const foo = [1, 2];
    const bar = foo;
    bar[0] = 9;
    console.log(foo[0], bar[0]); // => 9, 9
    複製代碼

References

  • 2.1 全部的賦值都用const,避免使用var. eslint: prefer-const, no-const-assignjava

    Why? 由於這個確保你不會改變你的初始值,重複引用會致使bug和代碼難以理解node

    // bad
    var a = 1;
    var b = 2;
    
    // good
    const a = 1;
    const b = 2;
    複製代碼

  • 2.2 若是你必定要對參數從新賦值,那就用let,而不是var. eslint: no-varjquery

    Why? 由於let是塊級做用域,而var是函數級做用域webpack

    // bad
    var count = 1;
    if (true) {
      count += 1;
    }
    
    // good, use the let.
    let count = 1;
    if (true) {
      count += 1;
    }
    複製代碼

  • 2.3 注意: letconst都是塊級做用域git

    // const 和 let 都只存在於它定義的那個塊級做用域
    {
      let a = 1;
      const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError
    複製代碼

Objects

  • 3.1 使用字面值建立對象. eslint: no-new-object程序員

    // bad
    const item = new Object();
    
    // good
    const item = {};
    複製代碼

  • 3.2 當建立一個帶有動態屬性名的對象時,用計算後屬性名es6

    Why? 這可使你將定義的全部屬性放在對象的一個地方.

    function getKey(k) {
      return `a key named ${k}`;
    }
    
    // bad
    const obj = {
      id: 5,
      name: 'San Francisco',
    };
    obj[getKey('enabled')] = true;
    
    // good getKey('enabled')是動態屬性名
    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

    Why? 這樣寫的更少且更可讀

    const lukeSkywalker = 'Luke Skywalker';
    
    // bad
    const obj = {
      lukeSkywalker: lukeSkywalker,
    };
    
    // good
    const obj = {
      lukeSkywalker,
    };
    複製代碼

  • 3.5 將你的全部縮寫放在對象聲明的開始.

    Why? 這樣也是爲了更方便的知道有哪些屬性用了縮寫.

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

  • 3.6 只對那些無效的標示使用引號 ''. eslint: quote-props

    Why? 一般咱們認爲這種方式主觀上易讀。他優化了代碼高亮,而且頁更容易被許多JS引擎壓縮。

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

  • 3.7 不要直接調用Object.prototype上的方法,如hasOwnProperty, propertyIsEnumerable, isPrototypeOf

    Why? 在一些有問題的對象上, 這些方法可能會被屏蔽掉 - 如:{ hasOwnProperty: false } - 或這是一個空對象Object.create(null)

    // bad
    console.log(object.hasOwnProperty(key));
    
    // good
    console.log(Object.prototype.hasOwnProperty.call(object, key));
    
    // best
    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。獲取對象指定的幾個屬性時,用對象的rest解構運算符[也是...運算符]更好。
    • 這一段不太好翻譯出來, 你們看下面的例子就懂了。^.^
// 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 es6擴展運算符 ...
const original = { a: 1, b: 2 };
// 淺拷貝
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

// rest 賦值運算符
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
複製代碼

Arrays

  • 4.1 用字面量賦值。 eslint: no-array-constructor

    // bad
    const items = new Array();
    
    // good
    const items = [];
    複製代碼

  • 4.2Array#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 += 1) {
      itemsCopy[i] = items[i];
    }
    
    // good
    const itemsCopy = [...items];
    複製代碼

  • 4.4... 運算符而不是Array.from來將一個可迭代的對象轉換成數組。

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

  • 4.5Array.from 去將一個類數組對象轉成一個數組。

    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.6Array.from 而不是 ... 運算符去作map遍歷。 由於這樣能夠避免建立一個臨時數組。

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

  • 4.7 在數組方法的回調函數中使用 return 語句。 若是函數體由一條返回一個表達式的語句組成, 而且這個表達式沒有反作用, 這個時候能夠忽略return,詳見 8.2. 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 - 沒有返回值, 由於在第一次迭代後acc 就變成undefined了
    [[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;
    });
    
    // 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;
    });
    複製代碼

  • 4.8 若是一個數組有不少行,在數組的 [ 後和 ] 前斷行。 請看下面示例

    // bad
    const arr = [
      [0, 1], [2, 3], [4, 5],
    ];
    
    const objectInArray = [{
      id: 1,
    }, {
      id: 2,
    }];
    
    const numberInArray = [
      1, 2,
    ];
    
    // good
    const arr = [[0, 1], [2, 3], [4, 5]];
    
    const objectInArray = [
      {
        id: 1,
      },
      {
        id: 2,
      },
    ];
    
    const numberInArray = [
      1,
      2,
    ];
    複製代碼

Destructuring

  • 5.1 用對象的解構賦值來獲取和使用對象某個或多個屬性值。 eslint: prefer-destructuring

    Why? 解構保存了這些屬性的臨時值/引用

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

  • 5.2 用數組解構.

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

  • 5.3 多個返回值用對象的解構,而不是數據解構。

    Why? 你能夠在後期添加新的屬性或者變換變量的順序而不會打破原有的調用

    // bad
    function processInput(input) {
      // 而後就是見證奇蹟的時刻
      return [left, right, top, bottom];
    }
    
    // 調用者須要想想返回值的順序
    const [left, __, top] = processInput(input);
    
    // good
    function processInput(input) {
      // oops, 奇蹟又發生了
      return { left, right, top, bottom };
    }
    
    // 調用者只須要選擇他想用的值就行了
    const { left, top } = processInput(input);
    複製代碼

Strings

  • 6.1 對string用單引號 '' 。 eslint: quotes

    // bad
    const name = "Capt. Janeway";
    
    // bad - 樣例應該包含插入文字或換行
    const name = `Capt. Janeway`;
    
    // good
    const name = 'Capt. Janeway';
    複製代碼

  • 6.2 超過100個字符的字符串不該該用string串聯成多行。

    Why? 被折斷的字符串工做起來是糟糕的並且使得代碼更不易被搜索。

    // 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 用字符串模板而不是字符串拼接來組織可編程字符串。 eslint: prefer-template template-curly-spacing

    Why? 模板字符串更具可讀性、語法簡潔、字符串插入參數。

    // 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(),他就是潘多拉盒子。 eslint: no-eval

  • 6.5 不要使用沒必要要的轉義字符。eslint: no-useless-escape

    Why? 反斜線可讀性差,因此他們只在必須使用時纔出現哦

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

Functions

  • 7.1 用命名函數表達式而不是函數聲明。eslint: func-style

    函數表達式: const func = function () {}

    函數聲明: function func() {}

    Why? 函數聲明時做用域被提早了,這意味着在一個文件裏函數很容易(太容易了)在其定義以前被引用。這樣傷害了代碼可讀性和可維護性。若是你發現一個函數又大又複雜,這個函數妨礙這個文件其餘部分的理解性,這可能就是時候把這個函數單獨抽成一個模塊了。別忘了給表達式顯示的命名,不用管這個名字是否是由一個肯定的變量推斷出來的,這消除了由匿名函數在錯誤調用棧產生的全部假設,這在現代瀏覽器和相似babel編譯器中很常見 (Discussion)

    Why? 這一段還不理解這種錯誤發生的場景,因此只能直譯過來了, 另附原文 Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions made about the Error’s call stack. (Discussion)

    // bad
    function foo() {
      // ...
    }
    
    // bad
    const foo = function () {
      // ...
    };
    
    // good
    // lexical name distinguished from the variable-referenced invocation(s)
    // 函數表達式名和聲明的函數名是不同的
    const short = function longUniqueMoreDescriptiveLexicalFoo() {
      // ...
    };
    複製代碼

  • 7.2 把當即執行函數包裹在圓括號裏。 eslint: wrap-iife

    Why? immediately invoked function expression = IIFE Why? 一個當即調用的函數表達式是一個單元 - 把它和他的調用者(圓括號)包裹起來,在括號中能夠清晰的地表達這些。 Why? 注意:在模塊化世界裏,你幾乎用不着 IIFE

    // immediately-invoked function expression (IIFE)
    (function () {
      console.log('Welcome to the Internet. Please follow me.');
    }());
    複製代碼

  • 7.3 不要在非函數塊(if、while等等)內聲明函數。把這個函數分配給一個變量。瀏覽器會容許你這樣作,但瀏覽器解析方式不一樣,這是一個壞消息。【詳見no-loop-func】 eslint: no-loop-func

  • 7.4 Note: 在ECMA-262中 [塊 block] 的定義是: 一系列的語句; 可是函數聲明不是一個語句。 函數表達式是一個語句。

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

  • 7.5 不要用arguments命名參數。他的優先級高於每一個函數做用域自帶的 arguments 對象, 這會致使函數自帶的 arguments 值被覆蓋

    // bad
    function foo(name, options, arguments) {
      // ...
    }
    
    // good
    function foo(name, options, args) {
      // ...
    }
    複製代碼

  • 7.6 不要使用arguments,用rest語法...代替。 eslint: prefer-rest-params

    Why? ...明確你想用那個參數。並且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) {
      // 不, 咱們不應改arguments
      // 第二: 若是 opts 的值爲 false, 它會被賦值爲 {}
      // 雖然你想這麼寫, 可是這個會帶來一些細微的bug
      opts = opts || {};
      // ...
    }
    
    // still bad
    function handleThings(opts) {
      if (opts === void 0) {
        opts = {};
      }
      // ...
    }
    
    // good
    function handleThings(opts = {}) {
      // ...
    }
    複製代碼

  • 7.8 默認參數避免反作用

    Why? 他會使人疑惑不解, 好比下面這個, a到底等於幾, 這個須要想一下。

    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 不要用函數構造器建立函數。 eslint: no-new-func

    Why? 以這種方式建立函數將相似於字符串 eval(),這會打開漏洞。

    // bad
    var add = new Function('a', 'b', 'return a + b');
    
    // still bad
    var subtract = Function('a', 'b', 'return a - b');
    複製代碼

  • 7.11 函數簽名部分要有空格。eslint: space-before-function-paren space-before-blocks

    Why? 統一性好,並且在你添加/刪除一個名字的時候不須要添加/刪除空格

    // bad
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // good
    const x = function () {};
    const y = function a() {};
    複製代碼

  • 7.12 不要改參數. eslint: no-param-reassign

    Why? 操做參數對象對原始調用者會致使意想不到的反作用。 就是不要改參數的數據結構,保留參數原始值和數據結構。

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

    Why? 參數從新賦值會致使意外行爲,尤爲是對 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.14spread操做符...去調用多變的函數更好。 eslint: prefer-spread

    Why? 這樣更清晰,你沒必要提供上下文,並且你不能輕易地用apply來組成new

    // 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,
    );
    複製代碼

Arrow Functions

  • 8.1 當你必定要用函數表達式(在回調函數裏)的時候就用箭頭表達式吧。 eslint: prefer-arrow-callback, arrow-spacing

    Why? 他建立了一個this的當前執行上下文的函數的版本,這一般就是你想要的;並且箭頭函數是更簡潔的語法

    Why? 何時不用箭頭函數: 若是你有一個至關複雜的函數,你可能會把這個邏輯移出到他本身的函數聲明裏。

    // bad
    [1, 2, 3].map(function (x) {
      const y = x + 1;
      return x * y;
    });
    
    // good
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
    複製代碼

  • 8.2 若是函數體由一個沒有反作用的表達式語句組成,刪除大括號和return。不然,繼續用大括號和 return 語句。 eslint: arrow-parens, arrow-body-style

    Why? 語法糖,當多個函數鏈在一塊兒的時候好讀

    // bad
    [1, 2, 3].map(number => {
      const nextNumber = number + 1;
      `A string containing the ${nextNumber}.`;
    });
    
    // good
    [1, 2, 3].map(number => `A string containing the ${number}.`);
    
    // good
    [1, 2, 3].map((number) => {
      const nextNumber = number + 1;
      return `A string containing the ${nextNumber}.`;
    });
    
    // good
    [1, 2, 3].map((number, index) => ({
      [index]: number
    }));
    
    // 表達式有反作用就不要用隱式return
    function foo(callback) {
      const val = callback();
      if (val === true) {
        // Do something if callback returns true
      }
    }
    
    let bool = false;
    
    // bad
    // 這種狀況會return bool = true, 很差
    foo(() => bool = true);
    
    // good
    foo(() => {
      bool = true;
    });
    複製代碼

  • 8.3 萬一表達式涉及多行,把他包裹在圓括號裏更可讀。

    Why? 這樣清晰的顯示函數的開始和結束

    // 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 若是你的函數只有一個參數而且函數體沒有大括號,就刪除圓括號。不然,參數老是放在圓括號裏。 注意: 一直用圓括號也是沒問題,只須要配置 「always」 option for eslint. eslint: arrow-parens

    Why? 這樣少一些混亂, 其實沒啥語法上的講究,就保持一個風格。

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

  • 8.5 避免箭頭函數(=>)和比較操做符(<=, >=)混淆. eslint: no-confusing-arrow

    // 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;
    };
    複製代碼

  • 8.6 在隱式return中強制約束函數體的位置, 就寫在箭頭後面。 eslint: implicit-arrow-linebreak

    // bad
    (foo) =>
      bar;
    
    (foo) =>
      (bar);
    
    // good
    (foo) => bar;
    (foo) => (bar);
    (foo) => (
       bar
    )
    複製代碼

Classes & Constructors

  • 9.1 經常使用class,避免直接操做prototype

    Why? 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.2extends實現繼承

    Why? 它是一種內置的方法來繼承原型功能而不打破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

    Why? 重複類成員會默默的執行最後一個 —— 重複自己也是一個bug

    // bad
    class Foo {
      bar() { return 1; }
      bar() { return 2; }
    }
    
    // good
    class Foo {
      bar() { return 1; }
    }
    
    // good
    class Foo {
      bar() { return 2; }
    }
    複製代碼

  • 9.7 除非外部庫或框架須要使用特定的非靜態方法,不然類方法應該使用this或被作成靜態方法。

做爲一個實例方法應該代表它根據接收者的屬性有不一樣的行爲。eslint: class-methods-use-this

```javascript
// bad
class Foo {
  bar() {
    console.log('bar');
  }
}

// good - this 被使用了 
class Foo {
  bar() {
    console.log(this.bar);
  }
}

// good - constructor 不必定要使用this
class Foo {
  constructor() {
    // ...
  }
}

// good - 靜態方法不須要使用 this
class Foo {
  static bar() {
    console.log('bar');
  }
}
```
複製代碼

Modules

  • 10.1 用(import/export) 模塊而不是無標準的模塊系統。你能夠隨時轉到你喜歡的模塊系統。

    Why? 模塊化是將來,讓咱們如今就開啓將來吧。

    // 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通配符, 就是 * 這種方式

    Why? 這確保你有單個默認的導出

    // bad
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';
    
    // good
    import AirbnbStyleGuide from './AirbnbStyleGuide';
    複製代碼

  • 10.3 不要直接從import中直接export

    Why? 雖然一行是簡潔的,有一個明確的方式進口和一個明確的出口方式來保證一致性。

    // 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 > Why? 從同一個路徑下import多行會使代碼難以維護

```javascript
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';

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

// good
import foo, {
  named1,
  named2,
} from 'foo';
```
複製代碼

  • 10.5 不要導出可變的東西

eslint: import/no-mutable-exports > Why? 變化一般都是須要避免,特別是當你要輸出可變的綁定。雖然在某些場景下可能須要這種技術,但總的來講應該導出常量。

```javascript
// bad
let foo = 3;
export { foo }

// good
const foo = 3;
export { foo }
```
複製代碼

  • 10.6 在一個單一導出模塊裏,用 export default 更好。

eslint: import/prefer-default-export

> Why? 鼓勵使用更多文件,每一個文件只作一件事情並導出,這樣可讀性和可維護性更好。

```javascript
// bad
export function foo() {}

// good
export default function foo() {}
```
複製代碼

  • 10.7 import 放在其餘全部語句以前。

eslint: import/first > Why? 讓import放在最前面防止意外行爲。

```javascript
// bad
import foo from 'foo';
foo.init();

import bar from 'bar';

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

foo.init();
```
複製代碼

  • 10.8 多行import應該縮進,就像多行數組和對象字面量

    Why? 花括號與樣式指南中每一個其餘花括號塊遵循相同的縮進規則,逗號也是。

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

  • 10.9 在import語句裏不容許Webpack loader語法

eslint: import/no-webpack-loader-syntax > Why? 一旦用Webpack語法在import裏會把代碼耦合到模塊綁定器。最好是在webpack.config.js裏寫webpack loader語法

```javascript
// 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 不要用遍歷器。用JavaScript高級函數代替for-infor-of。 eslint: no-iterator no-restricted-syntax

    Why? 這強調了咱們不可變的規則。 處理返回值的純函數比反作用更容易。

    Why? 用數組的這些迭代方法: map() / every() / filter() / find() / findIndex() / reduce() / some() / ... , 用對象的這些方法 Object.keys() / Object.values() / Object.entries() 去產生一個數組, 這樣你就能去遍歷對象了。

    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 如今不要用generator

    Why? 它在es5上支持的很差

  • 11.3 若是你必定要用,或者你忽略咱們的建議, 請確保它們的函數簽名空格是得當的。 eslint: generator-star-spacing

    Why? 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* () {
      // ...
    }
    複製代碼

Properties

  • 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');
    複製代碼

  • 12.3 作冪運算時用冪操做符 ** 。 eslint: no-restricted-properties.

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

Variables

  • 13.1constlet聲明變量。不這樣作會致使全局變量。 咱們想要避免污染全局命名空間。首長這樣警告咱們。 eslint: no-undef prefer-const

    // bad
    superPower = new SuperPower();
    
    // good
    const superPower = new SuperPower();
    複製代碼

  • 13.2 每一個變量都用一個 constlet 。 eslint: one-var

    Why? 這種方式很容易去聲明新的變量,你不用去考慮把;調換成,,或者引入一個只有標點的不一樣的變化。這種作法也能夠是你在調試的時候單步每一個聲明語句,而不是一下跳過全部聲明。

    // 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 const放一塊兒,let放一塊兒

    Why? 在你須要分配一個新的變量, 而這個變量依賴以前分配過的變量的時候,這種作法是有幫助的

    // 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 在你須要的地方聲明變量,可是要放在合理的位置

    Why? letconst 都是塊級做用域而不是函數級做用域

    // bad - unnecessary function call
    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 不要使用連接變量分配。 eslint: no-multi-assign

    Why? 連接變量分配建立隱式全局變量。

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

    Why? 根據eslint文檔,一元增量和減量語句受到自動分號插入的影響,而且可能會致使應用程序中的值遞增或遞減的無聲錯誤。 使用num + = 1而不是num ++num ++語句來表達你的值也是更有表現力的。 禁止一元增量和減量語句還會阻止您無心地預增/預減值,這也會致使程序出現意外行爲。

    // 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;
    複製代碼

  • 13.7 在賦值的時候避免在 = 前/後換行。 若是你的賦值語句超出 max-len, 那就用小括號把這個值包起來再換行。 eslint operator-linebreak.

    Why? 在 = 附近換行容易混淆這個賦值語句。

    // bad
    const foo =
      superLongLongLongLongLongLongLongLongFunctionName();
    
    // bad
    const foo
      = 'superLongLongLongLongLongLongLongLongString';
    
    // good
    const foo = (
      superLongLongLongLongLongLongLongLongFunctionName()
    );
    
    // good
    const foo = 'superLongLongLongLongLongLongLongLongString';
    複製代碼

  • 13.8 不容許有未使用的變量。 eslint: no-unused-vars

    Why? 一個聲明瞭但未使用的變量更像是因爲重構未完成產生的錯誤。這種在代碼中出現的變量會使閱讀者迷惑。

    // bad
    
    var some_unused_var = 42;
    
    // 寫了沒用
    var y = 10;
    y = 5;
    
    // 變量改了本身的值,也沒有用這個變量
    var z = 0;
    z = z + 1;
    
    // 參數定義了但未使用
    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' 即便沒有使用也能夠能夠被忽略, 由於這個有一個 rest 取值的屬性。
    // 這是從對象中抽取一個忽略特殊字段的對象的一種形式
    var { type, ...coords } = data;
    // 'coords' 如今就是一個沒有 'type' 屬性的 'data' 對象
    複製代碼

Hoisting

  • 14.1 var聲明會被提早到他的做用域的最前面,它分配的值尚未提早。constlet被賦予了新的調用概念時效區 —— Temporal Dead Zones (TDZ)。 重要的是要知道爲何 typeof再也不安全.

    // 咱們知道這個不會工做,假設沒有定義全局的notDefined
    function example() {
      console.log(notDefined); // => throws a ReferenceError
    }
    
    // 在你引用的地方以後聲明一個變量,他會正常輸出是由於變量做用域上升。
    // 注意: declaredButNotAssigned的值沒有上升
    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 匿名函數表達式和 var 狀況相同

    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');
      }
    }
    複製代碼
  • 詳情請見JavaScript Scoping & Hoisting by Ben Cherry.

Comparison Operators & Equality

  • 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 布爾值用縮寫,而字符串和數字要明確比較對象

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

  • 15.5casedefault分句裏用大括號建立一塊包含語法聲明的區域(e.g. let, const, function, and class). eslint rules: no-case-declarations.

    Why? 語法聲明在整個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;
    複製代碼

  • 15.8 用圓括號來混合這些操做符。 只有當標準的算術運算符(+, -, *, & /), 而且它們的優先級顯而易見時,能夠不用圓括號括起來。 eslint: no-mixed-operators

    Why? 這提升了可讀性,而且明確了開發者的意圖

    // bad
    const foo = a && b < 0 || c > 0 || d + 1 === 0;
    
    // bad
    const bar = a ** b - 5 % d;
    
    // bad
    // 別人會陷入(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;
    複製代碼

Blocks

  • 16.1 用大括號包裹多行代碼塊。 eslint: nonblock-statement-body-position

    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function foo() { return false; }
    
    // good
    function bar() {
      return false;
    }
    複製代碼

  • 16.2 if表達式的elseif的關閉大括號在一行。 eslint: brace-style

    // bad
    if (test) {
      thing1();
      thing2();
    }
    else {
      thing3();
    }
    
    // good
    if (test) {
      thing1();
      thing2();
    } else {
      thing3();
    }
    複製代碼

  • 16.3 若是 if 語句中老是須要用 return 返回, 那後續的 else 就不須要寫了。 if 塊中包含 return, 它後面的 else if 塊中也包含了 return, 這個時候就能夠把 return 分到多個 if 語句塊中。 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;
      }
    }
    複製代碼

Control Statements

  • 17.1 當你的控制語句(if, while 等)太長或者超過最大長度限制的時候, 把每個(組)判斷條件放在單獨一行裏。 邏輯操做符放在行首。

    Why? 把邏輯操做符放在行首是讓操做符的對齊方式和鏈式函數保持一致。這提升了可讀性,也讓複雜邏輯更容易看清楚。

    // bad
    if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
      thing1();
    }
    
    // bad
    if (foo === 123 &&
      bar === 'abc') {
      thing1();
    }
    
    // bad
    if (foo === 123
      && bar === 'abc') {
      thing1();
    }
    
    // bad
    if (
      foo === 123 &&
      bar === 'abc'
    ) {
      thing1();
    }
    
    // good
    if (
      foo === 123
      && bar === 'abc'
    ) {
      thing1();
    }
    
    // good
    if (
      (foo === 123 || bar === 'abc')
      && doesItLookGoodWhenItBecomesThatLong()
      && isThisReallyHappening()
    ) {
      thing1();
    }
    
    // good
    if (foo === 123 && bar === 'abc') {
      thing1();
    }
    複製代碼

  • 17.2 不要用選擇操做符代替控制語句。

    // bad
    !isRunning && startRunning();
    
    // good
    if (!isRunning) {
      startRunning();
    }
    複製代碼

⬆ back to top

Comments

  • 18.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;
    }
    複製代碼

  • 18.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;
    }
    複製代碼

  • 18.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;
    }
    複製代碼

  • 18.4 在你的註釋前使用FIXME'或TODO'前綴, 這有助於其餘開發人員快速理解你指出的須要從新訪問的問題, 或者您建議須要實現的問題的解決方案。 這些不一樣於常規註釋,由於它們是可操做的。 動做是FIXME: - 須要計算出來TODO: - 須要實現

  • 18.5// FIXME:給問題作註釋

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // FIXME: shouldn't use a global here
        total = 0;
      }
    }
    複製代碼

  • 18.6// TODO:去註釋問題的解決方案

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // TODO: total should be configurable by an options param
        this.total = 0;
      }
    }
    複製代碼

Whitespace

  • 19.1 tab用兩個空格. eslint: indent

    // bad
    function foo() {
    ∙∙∙∙const name;
    }
    
    // bad
    function bar() {
    ∙const name;
    }
    
    // good
    function baz() {
    ∙∙const name;
    }
    複製代碼

  • 19.2 在大括號前空一格。 eslint: space-before-blocks

    // 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',
    });
    複製代碼

  • 19.3 在控制語句(if, while 等)的圓括號前空一格。在函數調用和定義時,參數列表和函數名之間不空格。 eslint: keyword-spacing

    // bad
    if(isJedi) {
      fight ();
    }
    
    // good
    if (isJedi) {
      fight();
    }
    
    // bad
    function fight () {
      console.log ('Swooosh!');
    }
    
    // good
    function fight() {
      console.log('Swooosh!');
    }
    複製代碼

  • 19.4 用空格來隔開運算符。 eslint: space-infix-ops

    // bad
    const x=y+5;
    
    // good
    const x = y + 5;
    複製代碼

  • 19.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;↵
    複製代碼

  • 19.6 當出現長的方法鏈(>2個)時用縮進。用點開頭強調該行是一個方法調用,而不是一個新的語句。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);
    複製代碼

  • 19.7 在一個代碼塊後下一條語句前空一行。

    // 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;
    複製代碼

  • 19.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);
    }
    複製代碼

  • 19.9不要在代碼之間使用多個空白行填充。 eslint: no-multiple-empty-lines

    // bad
    class Person {
      constructor(fullName, email, birthday) {
        this.fullName = fullName;
    
    
        this.email = email;
    
    
        this.setAge(birthday);
      }
    
    
      setAge(birthday) {
        const today = new Date();
    
    
        const age = this.getAge(today, birthday);
    
    
        this.age = age;
      }
    
    
      getAge(today, birthday) {
        // ..
      }
    }
    
    // good
    class Person {
      constructor(fullName, email, birthday) {
        this.fullName = fullName;
        this.email = email;
        this.setAge(birthday);
      }
    
      setAge(birthday) {
        const today = new Date();
        const age = getAge(today, birthday);
        this.age = age;
      }
    
      getAge(today, birthday) {
        // ..
      }
    }
    複製代碼

  • 19.10 圓括號裏不要加空格。 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);
    }
    複製代碼

  • 19.11 方括號裏不要加空格。看示例。 eslint: array-bracket-spacing

    // bad
    const foo = [ 1, 2, 3 ];
    console.log(foo[ 0 ]);
    
    // good, 逗號分隔符仍是要空格的
    const foo = [1, 2, 3];
    console.log(foo[0]);
    複製代碼

  • 19.12 花括號里加空格。 eslint: object-curly-spacing

    // bad
    const foo = {clark: 'kent'};
    
    // good
    const foo = { clark: 'kent' };
    複製代碼

  • 19.13 避免一行代碼超過100個字符(包含空格)。

  • 注意: 對於上面——strings--line-length,長字符串不受此規則限制,不該分解。 eslint: max-len

    Why? 這樣確保可讀性和可維護性

    // 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.14 做爲語句的花括號內也要加空格 —— { 後和 } 前都須要空格。 eslint: block-spacing

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

  • 19.15 , 前不要空格, , 後須要空格。 eslint: comma-spacing

    // bad
    var foo = 1,bar = 2;
    var arr = [1 , 2];
    
    // good
    var foo = 1, bar = 2;
    var arr = [1, 2];
    複製代碼

  • 19.16 計算屬性內要空格。參考上述花括號和中括號的規則。 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.17 調用函數時,函數名和小括號之間不要空格。 eslint: func-call-spacing

    // bad
    func ();
    
    func
    ();
    
    // good
    func();
    複製代碼

  • 19.18 在對象的字面量屬性中, key value 之間要有空格。 eslint: key-spacing

    // bad
    var obj = { "foo" : 42 };
    var obj2 = { "foo":42 };
    
    // good
    var obj = { "foo": 42 };
    複製代碼

  • 19.20 避免出現多個空行。 在文件末尾只容許空一行。 eslint: no-multiple-empty-lines

    // bad
    var x = 1;
    
    
    
    var y = 2;
    
    // good
    var x = 1;
    
    var y = 2;
    複製代碼

Commas

  • 20.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',
    };
    複製代碼

  • 20.2 額外結尾逗號: eslint: comma-dangle

    Why? 這致使git diffs更清潔。 此外,像Babel這樣的轉換器會刪除轉換代碼中的額外的逗號,這意味着你沒必要擔憂舊版瀏覽器中的結尾逗號問題

    // bad - 沒有結尾逗號的 git diff
    const hero = {
         firstName: 'Florence',
    - lastName: 'Nightingale'
    + lastName: 'Nightingale',
    + inventorOf: ['coxcomb chart', 'modern nursing']
    };
    
    // good - 有結尾逗號的 git diff
    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
    )
    複製代碼

Semicolons

  • 21.1 Yup. eslint: semi

    Why? 當 JavaScript 遇到沒有分號結尾的一行,它會執行自動插入分號 Automatic Semicolon Insertion這一規則來決定行末是否加分號。若是JavaScript在你的斷行裏錯誤的插入了分號,就會出現一些古怪的行爲。當新的功能加到JavaScript裏後, 這些規則會變得更復雜難懂。顯示的結束語句,並經過配置代碼檢查去捕獲沒有帶分號的地方能夠幫助你防止這種錯誤。

    // bad
    (function () {
      const name = 'Skywalker'
      return name
    })()
    
    // good
    (function () {
      const name = 'Skywalker';
      return name;
    }());
    
    // good, 行首加分號,避免文件被鏈接到一塊兒時當即執行函數被當作變量來執行。
    ;(() => {
      const name = 'Skywalker';
      return name;
    }());
    複製代碼

    Read more.

Type Casting & Coercion

  • 22.1 在語句開始執行強制類型轉換。

  • 22.2 Strings: 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()
    
    // bad
    const totalScore = this.reviewScore.toString(); // 不保證返回string
    
    // good
    const totalScore = String(this.reviewScore);
    複製代碼

  • 22.3 Numbers: 用 Number 作類型轉換,parseInt轉換string常須要帶上基數。 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);
    複製代碼

  • 22.4 請在註釋中解釋爲何要用移位運算和你在作什麼。不管你作什麼狂野的事,好比因爲 parseInt 是你的性能瓶頸致使你必定要用移位運算。 請說明這個是由於性能緣由,

    // good
    /** * parseInt是代碼運行慢的緣由 * 用Bitshifting將字符串轉成數字使代碼運行效率大幅增加 */
    const val = inputValue >> 0;
    複製代碼

  • 22.5 注意: 用移位運算要當心. 數字使用64-位表示的,但移位運算經常返回的是32爲整形source)。移位運算對大於32位的整數會致使意外行爲。Discussion. 最大的32位整數是 2,147,483,647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
    複製代碼

  • 22.6 布爾:

    const age = 0;
    
    // bad
    const hasAge = new Boolean(age);
    
    // good
    const hasAge = Boolean(age);
    
    // best
    const hasAge = !!age;
    複製代碼

Naming Conventions

  • 23.1 避免用一個字母命名,讓你的命名可描述。 eslint: id-length

    // bad
    function q() {
      // ...
    }
    
    // good
    function query() {
      // ...
    }
    複製代碼

  • 23.2 用小駝峯式命名你的對象、函數、實例。 eslint: camelcase

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

  • 23.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',
    });
    複製代碼

  • 23.4 不要用前置或後置下劃線。 eslint: no-underscore-dangle

    Why? JavaScript 沒有私有屬性或私有方法的概念。儘管前置下劃線一般的概念上意味着「private」,事實上,這些屬性是徹底公有的,所以這部分也是你的API的內容。這一律念可能會致使開發者誤覺得更改這個不會致使崩潰或者不須要測試。 若是你想要什麼東西變成「private」,那就不要讓它在這裏出現。

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // good
    this.firstName = 'Panda';
    複製代碼

  • 23.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);
      };
    }
    複製代碼

  • 23.6 export default導出模塊A,則這個文件名也叫A.*, import 時候的參數也叫A。 大小寫徹底一致。

    // 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
    複製代碼

  • 23.7 當你export-default一個函數時,函數名用小駝峯,文件名須要和函數名一致。

    function makeStyleGuide() {
      // ...
    }
    
    export default makeStyleGuide;
    複製代碼

  • 23.8 當你export一個結構體/類/單例/函數庫/對象 時用大駝峯。

    const AirbnbStyleGuide = {
      es6: {
      }
    };
    
    export default AirbnbStyleGuide;
    複製代碼

  • 23.9 簡稱和縮寫應該所有大寫或所有小寫。

    Why? 名字都是給人讀的,不是爲了適應電腦的算法的。

    // bad
    import SmsContainer from './containers/SmsContainer';
    
    // bad
    const HttpRequests = [
      // ...
    ];
    
    // good
    import SMSContainer from './containers/SMSContainer';
    
    // good
    const HTTPRequests = [
      // ...
    ];
    
    // also good
    const httpRequests = [
      // ...
    ];
    
    // best
    import TextMessageContainer from './containers/TextMessageContainer';
    
    // best
    const requests = [
      // ...
    ];
    複製代碼

  • 23.10 你能夠用全大寫字母設置靜態變量,他須要知足三個條件。

    1. 導出變量
    2. const 定義的, 保證不能被改變
    3. 這個變量是可信的,他的子屬性都是不能被改變的

    Why? 這是一個附加工具,幫助開發者去辨識一個變量是否是不可變的。

    • 對於全部的 const 變量呢? —— 這個是沒必要要的。大寫變量不該該在同一個文件裏定義並使用, 它只能用來做爲導出變量。 贊同!
    • 那導出的對象呢? —— 大寫變量處在export的最高級(e.g. EXPORTED_OBJECT.key) 而且他包含的全部子屬性都是不可變的。
    // 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';
    
    // ---
    
    // 容許但不夠語義化
    export const apiKey = 'SOMEKEY';
    
    // 在大多數狀況下更好
    export const API_KEY = 'SOMEKEY';
    
    // ---
    
    // bad - 沒必要要的大寫鍵,沒有增長任何語言
    export const MAPPING = {
      KEY: 'value'
    };
    
    // good
    export const MAPPING = {
      key: 'value'
    };
    複製代碼

Accessors

  • 24.1 不須要使用屬性的訪問器函數。

  • 24.2 不要使用JavaScript的getters/setters,由於他們會產生反作用,而且難以測試、維護和理解。相反的,你能夠用 getVal()和setVal('hello')去創造你本身的accessor函數

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

  • 24.3 若是屬性/方法是boolean, 用 isVal()hasVal()

    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    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];
      }
    }
    複製代碼

Events

  • 25.1 經過哈希而不是原始值向事件裝載數據時(不管是DOM事件仍是像Backbone事件的不少屬性)。 這使得後續的貢獻者(程序員)向這個事件裝載更多的數據時不用去找或者更新每一個處理器。例如:

    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    // ...
    
    $(this).on('listingUpdated', (e, listingID) => {
      // do something with listingID
    });
    複製代碼

    prefer:

    // good
    $(this).trigger('listingUpdated', { listingID: listing.id });
    
    // ...
    
    $(this).on('listingUpdated', (e, data) => {
      // do something with data.listingID
    });
    複製代碼

jQuery

  • 26.1 jQuery對象用$變量表示。

    // bad
    const sidebar = $('.sidebar');
    
    // good
    const $sidebar = $('.sidebar');
    
    // good
    const $sidebarBtn = $('.sidebar-btn');
    複製代碼

  • 26.2 暫存jQuery查找

    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // good
    function setSidebar() {
      const $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
    複製代碼

  • 26.3 DOM查找用層疊式$('.sidebar ul') 或 父節點 > 子節點 $('.sidebar > ul'). jsPerf

  • 26.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();
    複製代碼

ES5 兼容性

ECMAScript 6+ (ES 2015+) Styles

  • 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 proposals, TC39尚未到 stage 3。

    Why? 它還不是最終版, 他可能還有不少變化,或者被撤銷。 咱們想要用的是 JavaScript, 提議還不是JavaScript。

Standard Library

標準庫中包含一些功能受損可是因爲歷史緣由遺留的工具類

  • 29.1Number.isNaN 代替全局的 isNaN. eslint: no-restricted-globals

    Why? 全局 isNaN 強制把非數字轉成數字, 而後對於任何強轉後爲 NaN 的變量都返回 true 若是你想用這個功能,就顯式的用它。

    // 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.2Number.isFinite 代替 isFinite. eslint: no-restricted-globals

    Why? 理由同上,會把一個非數字變量強轉成數字,而後作判斷。

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

Testing

  • 30.1 Yup.

    function foo() {
      return true;
    }
    複製代碼

  • 30.2 No, but seriously:
  • 不管用那個測試框架,你都須要寫測試。
  • 儘可能去寫不少小而美的純函數,減小突變的發生
  • 當心 stub 和 mock —— 這會讓你的測試變得脆弱。
  • 在 Airbnb 首選 mochatape 偶爾被用來測試一些小的,獨立的模塊。
  • 100%測試覆蓋率是咱們努力的目標,即使實際上不多達到。
  • 每當你修了一個bug, 都要寫一個迴歸測試。 一個bug修復了,沒有迴歸測試,極可能之後會再次出問題。
相關文章
相關標籤/搜索