JavaScript 風格指導(Airbnb版)

JavaScript 風格指導(Airbnb版)

用更合理的方式寫 JavaScriptjavascript

原文
翻譯自 Airbnb JavaScript Style Guidecss

目錄

  1. 類型
  2. 引用
  3. 對象
  4. 數組
  5. 解構
  6. 字符串
  7. 函數
  8. 箭頭函數
  9. 構造函數
  10. 模塊
  11. Iterators & Generators
  12. 屬性
  13. 變量
  14. 提高
  15. 比較運算符 & 等號
  16. 代碼塊
  17. 註釋
  18. 空白
  19. 逗號
  20. 分號
  21. 類型轉換
  22. 命名規則
  23. 存取器
  24. 事件
  25. jQuery
  26. ECMAScript 5 兼容性
  27. ECMAScript 6 編碼規範
  28. 測試
  29. 性能
  30. 資源
  31. 使用人羣
  32. 翻譯
  33. JavaScript 編碼規範說明
  34. 一塊兒來討論 JavaScript
  35. Contributors
  36. License

類型

  • 1.1 基本類型: 直接存取基本類型。html

    • 字符串
    • 數值
    • 布爾類型
    • null
    • undefined
    const foo = 1;
    let bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
  • 1.2 複製類型: 經過引用的方式存取複雜類型。java

    • 對象
    • 數組
    • 函數
    const foo = [1, 2];
    const bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9

⬆ 返回目錄node

引用

  • 2.1 對全部的引用使用 const ;避免使用 var

爲何?這能確保你沒法對引用從新賦值,也不會致使出現 bug 或難以理解。jquery

```javascript
// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;
```
  • 2.2 若是你必定須要可變更的引用,使用 let 代替 var

爲何?由於 let 是塊級做用域,而 var 是函數做用域。git

```javascript
// bad
var count = 1;
if (true) {
  count += 1;
}

// good, use the let.
let count = 1;
if (true) {
  count += 1;
}
```
  • 2.3 注意 letconst 都是塊級做用域。es6

    // const 和 let 只存在於它們被定義的區塊內。
    {
      let a = 1;
      const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError

⬆ 返回目錄github

對象

  • 3.1 使用字面值建立對象。web

    // bad
    const item = new Object();
    
    // good
    const item = {};
  • 3.2 若是你的代碼在瀏覽器環境下執行,別使用 保留字 做爲鍵值。這樣的話在 IE8 不會運行。 更多信息。 但在 ES6 模塊和服務器端中使用沒有問題。

    // bad
    const superman = {
      default: { clark: 'kent' },
      private: true,
    };
    
    // good
    const superman = {
      defaults: { clark: 'kent' },
      hidden: true,
    };
  • 3.3 使用同義詞替換須要使用的保留字。

    // bad
    const superman = {
      class: 'alien',
    };
    
    // bad
    const superman = {
      klass: 'alien',
    };
    
    // good
    const superman = {
      type: 'alien',
    };

  • 3.4 建立有動態屬性名的對象時,使用可被計算的屬性名稱。

爲何?由於這樣可讓你在一個地方定義全部的對象屬性。

```javascript
function getKey(k) {
  return `a key named ${k}`;
}

// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};
```

  • 3.5 使用對象方法的簡寫。

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

  • 3.6 使用對象屬性值的簡寫。

爲何?由於這樣更短更有描述性。

```javascript
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
  lukeSkywalker,
};
```
  • 3.7 在對象屬性聲明前把簡寫的屬性分組。

爲何?由於這樣能清楚地看出哪些屬性使用了簡寫。

```javascript
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

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

// good
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJedisWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
};
```

⬆ 返回目錄

數組

  • 4.1 使用字面值建立數組。

    // bad
    const items = new Array();
    
    // good
    const items = [];
  • 4.2 向數組添加元素時使用 Arrary#push 替代直接賦值。

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

  • 4.3 使用拓展運算符 ... 複製數組。

    // bad
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    const itemsCopy = [...items];
  • 4.4 使用 Array#from 把一個類數組對象轉換成數組。

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

⬆ 返回目錄

解構

  • 5.1 使用解構存取和使用多屬性對象。

爲何?由於解構能減小臨時引用屬性。

```javascript
// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

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

// best
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}
```
  • 5.2 對數組使用解構賦值。

    const arr = [1, 2, 3, 4];
    
    // bad
    const first = arr[0];
    const second = arr[1];
    
    // good
    const [first, second] = arr;
  • 5.3 須要回傳多個值時,使用對象解構,而不是數組解構。

    爲何?增長屬性或者改變排序不會改變調用時的位置。

    // bad
    function processInput(input) {
      // then a miracle occurs
      return [left, right, top, bottom];
    }
    
    // 調用時須要考慮回調數據的順序。
    const [left, __, top] = processInput(input);
    
    // good
    function processInput(input) {
      // then a miracle occurs
      return { left, right, top, bottom };
    }
    
    // 調用時只選擇須要的數據
    const { left, right } = processInput(input);

⬆ 返回目錄

Strings

  • 6.1 字符串使用單引號 ''

    // bad
    const name = "Capt. Janeway";
    
    // good
    const name = 'Capt. Janeway';
  • 6.2 字符串超過 80 個字節應該使用字符串鏈接號換行。
  • 6.3 注:過分使用字串鏈接符號可能會對性能形成影響。jsPerf討論.

    // bad
    const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    // bad
    const errorMessage = 'This is a super long error that was thrown because \
    of Batman. When you stop to think about how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // good
    const errorMessage = 'This is a super long error that was thrown because ' +
      'of Batman. When you stop to think about how Batman had anything to do ' +
      'with this, you would get nowhere fast.';

  • 6.4 程序化生成字符串時,使用模板字符串代替字符串鏈接。

爲何?模板字符串更爲簡潔,更具可讀性。

```javascript
// bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

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

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}
```

⬆ 返回目錄

函數

  • 7.1 使用函數聲明代替函數表達式。

爲何?由於函數聲明是可命名的,因此他們在調用棧中更容易被識別。此外,函數聲明會把整個函數提高(hoisted),而函數表達式只會把函數的引用變量名提高。這條規則使得箭頭函數能夠取代函數表達式。

```javascript
// bad
const foo = function () {
};

// good
function foo() {
}
```
  • 7.2 函數表達式:

    // 當即調用的函數表達式 (IIFE)
    (() => {
      console.log('Welcome to the Internet. Please follow me.');
    })();
  • 7.3 永遠不要在一個非函數代碼塊(ifwhile 等)中聲明一個函數,把那個函數賦給一個變量。瀏覽器容許你這麼作,但它們的解析表現不一致。
  • 7.4 注意: ECMA-262 把 block 定義爲一組語句。函數聲明不是語句。閱讀 ECMA-262 關於這個問題的說明

    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    let test;
    if (currentUser) {
      test = () => {
        console.log('Yup.');
      };
    }
  • 7.5 永遠不要把參數命名爲 arguments。這將取代原來函數做用域內的 arguments 對象。

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

  • 7.6 不要使用 arguments。能夠選擇 rest 語法 ... 替代。

爲何?使用 ... 能明確你要傳入的參數。另外 rest 參數是一個真正的數組,而 arguments 是一個類數組。

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

// good
function concatenateAll(...args) {
  return args.join('');
}
```

  • 7.7 直接給函數的參數指定默認值,不要使用一個變化的函數參數。

    // really bad
    function handleThings(opts) {
      // 不!咱們不該該改變函數參數。
      // 更加糟糕: 若是參數 opts 是 false 的話,它就會被設定爲一個對象。
      // 但這樣的寫法會形成一些 Bugs。
      //(譯註:例如當 opts 被賦值爲空字符串,opts 仍然會被下一行代碼設定爲一個空對象。)
      opts = opts || {};
      // ...
    }
    
    // still bad
    function handleThings(opts) {
      if (opts === void 0) {
        opts = {};
      }
      // ...
    }
    
    // good
    function handleThings(opts = {}) {
      // ...
    }
  • 7.8 直接給函數參數賦值時須要避免反作用。

爲何?由於這樣的寫法讓人感到很困惑。

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

⬆ 返回目錄

箭頭函數

  • 8.1 當你必須使用函數表達式(或傳遞一個匿名函數)時,使用箭頭函數符號。

爲何?由於箭頭函數創造了新的一個 this 執行環境(譯註:參考 Arrow functions - JavaScript | MDNES6 arrow functions, syntax and lexical scoping),一般狀況下都能知足你的需求,並且這樣的寫法更爲簡潔。

爲何不?若是你有一個至關複雜的函數,你或許能夠把邏輯部分轉移到一個函數聲明上。

```javascript
// bad
[1, 2, 3].map(function (x) {
  return x * x;
});

// good
[1, 2, 3].map((x) => {
  return x * x;
});
```
  • 8.2 若是一個函數適合用一行寫出而且只有一個參數,那就把花括號、圓括號和 return 都省略掉。若是不是,那就不要省略。

爲何?語法糖。在鏈式調用中可讀性很高。

爲何不?當你打算回傳一個對象的時候。

```javascript
// good
[1, 2, 3].map(x => x * x);

// good
[1, 2, 3].reduce((total, n) => {
  return total + n;
}, 0);
```

⬆ 返回目錄

構造器

  • 9.1 老是使用 class。避免直接操做 prototype

爲何? 由於 class 語法更爲簡潔更易讀。

```javascript
// bad
function Queue(contents = []) {
  this._queue = [...contents];
}
Queue.prototype.pop = function() {
  const value = this._queue[0];
  this._queue.splice(0, 1);
  return value;
}


// good
class Queue {
  constructor(contents = []) {
    this._queue = [...contents];
  }
  pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
  }
}
```
  • 9.2 使用 extends 繼承。

爲何?由於 extends 是一個內建的原型繼承方法而且不會破壞 instanceof

```javascript
// 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()}`;
      }
    }

⬆ 返回目錄

模塊

  • 10.1 老是使用模組 (import/export) 而不是其餘非標準模塊系統。你能夠編譯爲你喜歡的模塊系統。

爲何?模塊就是將來,讓咱們開始邁向將來吧。

```javascript
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
```
  • 10.2 不要使用通配符 import。

爲何?這樣能確保你只有一個默認 export。

```javascript
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
```
  • 10.3 不要從 import 中直接 export。

爲何?雖然一行代碼簡潔明瞭,但讓 import 和 export 各司其職讓事情能保持一致。

```javascript
// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
```

⬆ 返回目錄

Iterators and Generators

  • 11.1 不要使用 iterators。使用高階函數例如 map()reduce() 替代 for-of

爲何?這增強了咱們不變的規則。處理純函數的回調值更易讀,這比它帶來的反作用更重要。

```javascript
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;
```
  • 11.2 如今還不要使用 generators。

爲何?由於它們如今還無法很好地編譯到 ES5。

⬆ 返回目錄

屬性

  • 12.1 使用 . 來訪問對象的屬性。

    const luke = {
      jedi: true,
      age: 28,
    };
    
    // bad
    const isJedi = luke['jedi'];
    
    // good
    const isJedi = luke.jedi;
  • 12.2 當經過變量訪問屬性時使用中括號 []

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

⬆ 返回目錄

變量

  • 13.1 一直使用 const 來聲明變量,若是不這樣作就會產生全局變量。咱們須要避免全局命名空間的污染。地球隊長已經警告過咱們了。(譯註:全局,global 亦有全球的意思。地球隊長的責任是保衛地球環境,因此他警告咱們不要形成「全球」污染。)

    // bad
    superPower = new SuperPower();
    
    // good
    const superPower = new SuperPower();
  • 13.2 使用 var 聲明每個變量。

    爲何?增長新變量將變的更加容易,並且你永遠不用再擔憂調換錯 ;,

    // bad
    const items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
    // bad
    // (compare to above, and try to spot the mistake)
    const items = getItems(),
        goSportsTeam = true;
        dragonball = 'z';
    
    // good
    const items = getItems();
    const goSportsTeam = true;
    const dragonball = 'z';
  • 13.3 將全部的 constlet 分組

爲何?當你須要把已賦值變量賦值給未賦值變量時很是有用。

```javascript
// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
```
  • 13.4 在你須要的地方給變量賦值,但請把它們放在一個合理的位置。

爲何?letconst 是塊級做用域而不是函數做用域。

```javascript
// good
function() {
  test();
  console.log('doing stuff..');

  //..other stuff..

  const name = getName();

  if (name === 'test') {
    return false;
  }

  return name;
}

// bad - unnecessary function call
function(hasName) {
  const name = getName();

  if (!hasName) {
    return false;
  }

  this.setFirstName(name);

  return true;
}

// good
function(hasName) {
  if (!hasName) {
    return false;
  }

  const name = getName();
  this.setFirstName(name);

  return true;
}
```

⬆ 返回目錄

Hoisting

  • 14.1 var 聲明會被提高至該做用域的頂部,但它們賦值不會提高。letconst 被賦予了一種稱爲「暫時性死區(Temporal Dead Zones, TDZ)」的概念。這對於瞭解爲何 type of 再也不安全至關重要。

    // 咱們知道這樣運行不了 
    // (假設 notDefined 不是全局變量)
    function example() {
      console.log(notDefined); // => throws a ReferenceError
    }
    
    // 因爲變量提高的緣由,
    // 在引用變量後再聲明變量是能夠運行的。
    // 注:變量的賦值 `true` 不會被提高。
    function example() {
      console.log(declaredButNotAssigned); // => undefined
      var declaredButNotAssigned = true;
    }
    
    // 編譯器會把函數聲明提高到做用域的頂層,
    // 這意味着咱們的例子能夠改寫成這樣:
    function example() {
      let declaredButNotAssigned;
      console.log(declaredButNotAssigned); // => undefined
      declaredButNotAssigned = true;
    }
    
    // 使用 const 和 let
    function example() {
      console.log(declaredButNotAssigned); // => throws a ReferenceError
      console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
      const declaredButNotAssigned = true;
    }
  • 14.2 匿名函數表達式的變量名會被提高,但函數內容並不會。

    function example() {
      console.log(anonymous); // => undefined
    
      anonymous(); // => TypeError anonymous is not a function
    
      var anonymous = function() {
        console.log('anonymous function expression');
      };
    }
  • 14.3 命名的函數表達式的變量名會被提高,但函數名和函數函數內容並不會。

    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
    
      superPower(); // => ReferenceError superPower is not defined
    
      var named = function superPower() {
        console.log('Flying');
      };
    }
    
    // the same is true when the function name
    // is the same as the variable name.
    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
    
      var named = function named() {
        console.log('named');
      }
    }
  • 14.4 函數聲明的名稱和函數體都會被提高。

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

⬆ 返回目錄

比較運算符 & 等號

  • 15.1 優先使用 ===!== 而不是 ==!=.
  • 15.2 條件表達式例如 if 語句經過抽象方法 ToBoolean 強制計算它們的表達式而且老是遵照下面的規則:

    • 對象 被計算爲 true
    • Undefined 被計算爲 false
    • Null 被計算爲 false
    • 布爾值 被計算爲 布爾的值
    • 數字 若是是 +0、-0、或 NaN 被計算爲 false, 不然爲 true
    • 字符串 若是是空字符串 '' 被計算爲 false,不然爲 true
    if ([0]) {
      // true
      // An array is an object, objects evaluate to true
    }
  • 15.3 使用簡寫。

    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
  • 15.4 想了解更多信息,參考 Angus Croll 的 Truth Equality and JavaScript

⬆ 返回目錄

代碼塊

  • 16.1 使用大括號包裹全部的多行代碼塊。

    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
  • 16.2 若是經過 ifelse 使用多行代碼塊,把 else 放在 if 代碼塊關閉括號的同一行。

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

⬆ 返回目錄

註釋

  • 17.1 使用 /** ... */ 做爲多行註釋。包含描述、指定全部參數和返回值的類型和值。

    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param {String} tag
     * @return {Element} element
     */
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
  • 17.2 使用 // 做爲單行註釋。在評論對象上面另起一行使用單行註釋。在註釋前插入空行。

    // bad
    const active = true;  // is current tab
    
    // good
    // is current tab
    const active = true;
    
    // bad
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      const type = this._type || 'no type';
    
      return type;
    }
    
    // good
    function getType() {
      console.log('fetching type...');
    
      // set the default type to 'no type'
      const type = this._type || 'no type';
    
      return type;
    }
  • 17.3 給註釋增長 FIXMETODO 的前綴能夠幫助其餘開發者快速瞭解這是一個須要複查的問題,或是給須要實現的功能提供一個解決方式。這將有別於常見的註釋,由於它們是可操做的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

  • 17.4 使用 // FIXME: 標註問題。

    class Calculator {
      constructor() {
        // FIXME: shouldn't use a global here
        total = 0;
      }
    }
  • 17.5 使用 // TODO: 標註問題的解決方式。

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

⬆ 返回目錄

空白

  • 18.1 使用 2 個空格做爲縮進。

    // bad
    function() {
    ∙∙∙∙const name;
    }
    
    // bad
    function() {
    ∙const name;
    }
    
    // good
    function() {
    ∙∙const name;
    }
  • 18.2 在花括號前放一個空格。

    // bad
    function test(){
      console.log('test');
    }
    
    // good
    function test() {
      console.log('test');
    }
    
    // bad
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog',
    });
    
    // good
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog',
    });
  • 18.3 在控制語句(ifwhile 等)的小括號前放一個空格。在函數調用及聲明中,不在函數的參數列表前加空格。

    // bad
    if(isJedi) {
      fight ();
    }
    
    // good
    if (isJedi) {
      fight();
    }
    
    // bad
    function fight () {
      console.log ('Swooosh!');
    }
    
    // good
    function fight() {
      console.log('Swooosh!');
    }
  • 18.4 使用空格把運算符隔開。

    // bad
    const x=y+5;
    
    // good
    const x = y + 5;
  • 18.5 在文件末尾插入一個空行。

    // bad
    (function(global) {
      // ...stuff...
    })(this);
    // bad
    (function(global) {
      // ...stuff...
    })(this);↵
    ↵
    // good
    (function(global) {
      // ...stuff...
    })(this);↵
  • 18.5 在使用長方法鏈時進行縮進。使用前面的點 . 強調這是方法調用而不是新語句。

    // 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').class('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);
  • 18.6 在塊末和新語句前插入空行。

    // bad
    if (foo) {
      return bar;
    }
    return baz;
    
    // good
    if (foo) {
      return bar;
    }
    
    return baz;
    
    // bad
    const obj = {
      foo() {
      },
      bar() {
      },
    };
    return obj;
    
    // good
    const obj = {
      foo() {
      },
    
      bar() {
      },
    };
    
    return obj;

⬆ 返回目錄

逗號

  • 19.1 行首逗號:不須要

    // bad
    const story = [
        once
      , upon
      , aTime
    ];
    
    // good
    const story = [
      once,
      upon,
      aTime,
    ];
    
    // bad
    const hero = {
        firstName: 'Ada'
      , lastName: 'Lovelace'
      , birthYear: 1815
      , superPower: 'computers'
    };
    
    // good
    const hero = {
      firstName: 'Ada',
      lastName: 'Lovelace',
      birthYear: 1815,
      superPower: 'computers',
    };
  • 19.2 增長結尾的逗號: 須要

爲何? 這會讓 git diffs 更乾淨。另外,像 babel 這樣的轉譯器會移除結尾多餘的逗號,也就是說你沒必要擔憂老舊瀏覽器的尾逗號問題

```javascript
// bad - git diff without trailing comma
const hero = {
     firstName: 'Florence',
-    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb graph', 'modern nursing']
}

// good - git diff with trailing comma
const hero = {
     firstName: 'Florence',
     lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
}

// bad
const hero = {
  firstName: 'Dana',
  lastName: 'Scully'
};

const heroes = [
  'Batman',
  'Superman'
];

// good
const hero = {
  firstName: 'Dana',
  lastName: 'Scully',
};

const heroes = [
  'Batman',
  'Superman',
];
```

⬆ 返回目錄

分號

  • 20.1 使用分號

    // bad
    (function() {
      const name = 'Skywalker'
      return name
    })()
    
    // good
    (() => {
      const name = 'Skywalker';
      return name;
    })();
    
    // good (防止函數在兩個 IIFE 合併時被當成一個參數)
    ;(() => {
      const name = 'Skywalker';
      return name;
    })();

    Read more.

⬆ 返回目錄

類型轉換

  • 21.1 在語句開始時執行類型轉換。
  • 21.2 字符串:

    //  => this.reviewScore = 9;
    
    // bad
    const totalScore = this.reviewScore + '';
    
    // good
    const totalScore = String(this.reviewScore);
  • 21.3 對數字使用 parseInt 轉換,並帶上類型轉換的基數。

    const inputValue = '4';
    
    // bad
    const val = new Number(inputValue);
    
    // bad
    const val = +inputValue;
    
    // bad
    const val = inputValue >> 0;
    
    // bad
    const val = parseInt(inputValue);
    
    // good
    const val = Number(inputValue);
    
    // good
    const val = parseInt(inputValue, 10);
  • 21.4 若是由於某些緣由 parseInt 成爲你所作的事的瓶頸而須要使用位操做解決性能問題時,留個註釋說清楚緣由和你的目的。

    // good
    /**
     * 使用 parseInt 致使個人程序變慢,
     * 改爲使用位操做轉換數字快多了。
     */
    const val = inputValue >> 0;
  • 21.5 注: 當心使用位操做運算符。數字會被當成 64 位值,可是位操做運算符老是返回 32 位的整數(參考)。位操做處理大於 32 位的整數值時還會致使意料以外的行爲。關於這個問題的討論。最大的 32 位整數是 2,147,483,647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
  • 21.6 布爾:

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

⬆ 返回目錄

命名規則

  • 22.1 避免單字母命名。命名應具有描述性。

    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
  • 22.2 使用駝峯式命名對象、函數和實例。

    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}
  • 22.3 使用帕斯卡式命名構造函數或類。

    // bad
    function user(options) {
      this.name = options.name;
    }
    
    const bad = new user({
      name: 'nope',
    });
    
    // good
    class User {
      constructor(options) {
        this.name = options.name;
      }
    }
    
    const good = new User({
      name: 'yup',
    });
  • 22.4 使用下劃線 _ 開頭命名私有屬性。

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
  • 22.5 別保存 this 的引用。使用箭頭函數或 Function#bind。

    // bad
    function foo() {
      const self = this;
      return function() {
        console.log(self);
      };
    }
    
    // bad
    function foo() {
      const that = this;
      return function() {
        console.log(that);
      };
    }
    
    // good
    function foo() {
      return () => {
        console.log(this);
      };
    }
  • 22.6 若是你的文件只輸出一個類,那你的文件名必須和類名徹底保持一致。

    // file contents
    class CheckBox {
      // ...
    }
    export default CheckBox;
    
    // in some other file
    // bad
    import CheckBox from './checkBox';
    
    // bad
    import CheckBox from './check_box';
    
    // good
    import CheckBox from './CheckBox';
  • 22.7 當你導出默認的函數時使用駝峯式命名。你的文件名必須和函數名徹底保持一致。

    function makeStyleGuide() {
    }
    
    export default makeStyleGuide;
  • 22.8 當你導出單例、函數庫、空對象時使用帕斯卡式命名。

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

⬆ 返回目錄

存取器

  • 23.1 屬性的存取函數不是必須的。
  • 23.2 若是你須要存取函數時使用 getVal()setVal('hello')

    // bad
    dragon.age();
    
    // good
    dragon.getAge();
    
    // bad
    dragon.age(25);
    
    // good
    dragon.setAge(25);
  • 23.3 若是屬性是布爾值,使用 isVal()hasVal()

    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
  • 23.4 建立 get()set() 函數是能夠的,但要保持一致。

    class Jedi {
      constructor(options = {}) {
        const lightsaber = options.lightsaber || 'blue';
        this.set('lightsaber', lightsaber);
      }
    
      set(key, val) {
        this[key] = val;
      }
    
      get(key) {
        return this[key];
      }
    }

⬆ 返回目錄

事件

  • 24.1 當給時間附加數據時(不管是 DOM 事件仍是私有事件),傳入一個哈希而不是原始值。這樣可讓後面的貢獻者增長更多數據到事件數據而無需找出並更新事件的每個處理器。例如,很差的寫法:

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

    更好的寫法:

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

⬆ 返回目錄

jQuery

  • 25.1 使用 $ 做爲存儲 jQuery 對象的變量名前綴。

    // bad
    const sidebar = $('.sidebar');
    
    // good
    const $sidebar = $('.sidebar');
  • 25.2 緩存 jQuery 查詢。

    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...stuff...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // good
    function setSidebar() {
      const $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...stuff...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
  • 25.3 對 DOM 查詢使用層疊 $('.sidebar ul') 或 父元素 > 子元素 $('.sidebar > ul')jsPerf
  • 25.4 對有做用域的 jQuery 對象查詢使用 find

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

⬆ 返回目錄

ECMAScript 5 兼容性

⬆ 返回目錄

ECMAScript 6 規範

  • 27.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. Iterators and Generators
  13. Modules

⬆ 返回目錄

測試

  • 28.1 Yup.

    function() {
      return true;
    }

⬆ 返回目錄

性能

⬆ 返回目錄

資源

Learning ES6

Read This

Tools

Other Styleguides

Other Styles

Further Reading

Books

Blogs

Podcasts

⬆ 返回目錄

使用人羣

This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list.

⬆ 返回目錄

翻譯

This style guide is also available in other languages:

JavaScript 編碼規範說明

一塊兒來討論 JavaScript

Contributors

License

(The MIT License)

Copyright (c) 2014 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.

⬆ 返回目錄

相關文章
相關標籤/搜索