禪與JavaScript程式設計藝術,ZenandTheArtofJavaScriptProgramming

程式設計師詩人發表於2018-06-15

禪與 JavaScript 程式設計藝術

Zen and The Art of JavaScript Programming

參考資料: Airbnb JavaScript Style Guide

目錄

  1. 型別
  2. 引用
  3. 物件
  4. 陣列
  5. 解構
  6. 字串
  7. 函式
  8. 箭頭函式
  9. 建構函式
  10. 模組
  11. 迭代器和生成器
  12. 屬性
  13. 變數
  14. 提升
  15. 比較運算子和等號
  16. 程式碼塊
  17. 註釋
  18. 空白
  19. 逗號
  20. 分號
  21. 型別轉換
  22. 命名規則
  23. 存取器
  24. 事件
  25. jQuery
  26. ECMAScript 5 相容性
  27. ECMAScript 6 編碼規範
  28. 測試
  29. 效能
  30. 相關資源
  31. 使用情況
  32. 其他翻譯
  33. JavaScript 編碼規範說明
  34. 討論 JavaScript
  35. 貢獻者
  36. 許可協議

型別

  • 1.1 基本型別: js基本型別(不可變型別):

    • 字串
    • 數值
    • 布林型別
    • null
    • undefined
    const foo = 1;
    let bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
    
  • 1.2 複雜(引用)型別: 通過引用的方式存取複雜型別: 一切皆是對映。

    • 物件
    • 陣列
    • 函式
    const foo = [1, 2];
    const bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9
    

Variable types

Computers are sophisticated and can make use of more complex variables than just numbers. This is where variable types come in. Variables come in several types and different languages support different types.

The most common types are:

Numbers
Float: a number, like 1.21323, 4, -33.5, 100004 or 0.123
Integer: a number like 1, 12, -33, 140 but not 1.233

String: a line of text like “boat”, “elephant” or “damn, you are tall!”

Boolean: either true or false, but nothing else

Arrays: a collection of values like: 1,2,3,4,`I am bored now`

Objects: a representation of a more complex object

null: a variable that contains null contains no valid Number, String, Boolean, Array, or Object

undefined: the undefined value is obtained when you use an object property that does not exist, or a variable that has been declared, but has no value assigned to it.

JavaScript is a “loosely typed” language, which means that you don`t have to explicitly declare what type of data the variables are. You just need to use the var keyword to indicate that you are declaring a variable, and the interpreter will work out what data type you are using from the context, and use of quotes.

引用

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

    為什麼?這能確保你無法對引用重新賦值,也不會導致出現 bug 或難以理解。

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

    為什麼?因為 let 是塊級作用域,而 var 是函式作用域。

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

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

物件

  • 3.1 使用字面值建立物件。

    // 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 建立有動態屬性名的物件時,使用可被計算的屬性名稱。

    為什麼?因為這樣可以讓你在一個地方定義所有的物件屬性。

    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 使用物件屬性值的簡寫。

    為什麼?因為這樣更短更有描述性。

    const lukeSkywalker = `Luke Skywalker`;
    
    // bad
    const obj = {
      lukeSkywalker: lukeSkywalker,
    };
    
    // good
    const obj = {
      lukeSkywalker,
    };
    
  • 3.7 在物件屬性宣告前把簡寫的屬性分組。

    為什麼?因為這樣能清楚地看出哪些屬性使用了簡寫。

    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 使用解構存取和使用多屬性物件。

    為什麼?因為解構能減少臨時引用屬性。

    // 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 程式化生成字串時,使用模板字串代替字串連線。

    為什麼?模板字串更為簡潔,更具可讀性。

    // 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),而函式表示式只會把函式的引用變數名提升。這條規則使得箭頭函式可以取代函式表示式。

    // 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 是一個類陣列。

    // 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),通常情況下都能滿足你的需求,而且這樣的寫法更為簡潔。

    為什麼不?如果你有一個相當複雜的函式,你或許可以把邏輯部分轉移到一個函式宣告上。

    // 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 都省略掉。如果不是,那就不要省略。

    為什麼?語法糖。在鏈式呼叫中可讀性很高。

    為什麼不?當你打算回傳一個物件的時候。

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

構造器

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

    為什麼? 因為 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.2 使用 extends 繼承。

    為什麼?因為 extends 是一個內建的原型繼承方法並且不會破壞 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()}`;
      }
    }
    

模組

  • 10.1 總是使用模組 (import/export) 而不是其他非標準模組系統。你可以編譯為你喜歡的模組系統。

    為什麼?模組就是未來,讓我們開始邁向未來吧。

    // bad
    const AirbnbStyleGuide = require(`./AirbnbStyleGuide`);
    module.exports = AirbnbStyleGuide.es6;
    
    // ok
    import AirbnbStyleGuide from `./AirbnbStyleGuide`;
    export default AirbnbStyleGuide.es6;
    
    // best
    import { es6 } from `./AirbnbStyleGuide`;
    export default es6;
    
  • 10.2 不要使用萬用字元 import。

    為什麼?這樣能確保你只有一個預設 export。

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

    為什麼?雖然一行程式碼簡潔明瞭,但讓 import 和 export 各司其職讓事情能保持一致。

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

    為什麼?這加強了我們不變的規則。處理純函式的回撥值更易讀,這比它帶來的副作用更重要。

    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。 (譯者注:目前(2016/03) Chrome 和 Node.js 的穩定版本都已支援 generators)

屬性

  • 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 使用 const 宣告每一個變數。

    為什麼?增加新變數將變的更加容易,而且你永遠不用再擔心調換錯 ;,

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

    為什麼?當你需要把已賦值變數賦值給未賦值變數時非常有用。

    // 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 是塊級作用域而不是函式作用域。

    // 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 這樣的轉譯器會移除結尾多餘的逗號,也就是說你不必擔心老舊瀏覽器的尾逗號問題

    // 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`;
    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. 箭頭函式
  2. 物件方法簡寫
  3. 物件屬性簡寫
  4. 物件中的可計算屬性
  5. 模板字串
  6. 解構
  7. 預設引數
  8. Rest
  9. 陣列 Spreads
  10. Let 及 Const
  11. 迭代器和生成器
  12. 模組

測試

  • 28.1 Yup.

    function() {
      return true;
    }
    

效能

相關資源(英文)

瞭解 ES6

看看這個

工具

其他風格指南

其他風格

擴充閱讀

書籍

部落格

播客

使用情況

下列組織應用這份風格指南。

翻譯

這份風格指南也有其他語言的譯本:

JavaScript 編碼規範說明

討論 JavaScript

  • 歡迎到 gitter 與我們聊天(英文)。

貢獻者

許可協議

(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.

修訂

我們鼓勵您派生本指南和更改規則以適應您的團隊需求。您可以在下方列出對本風格指南的修改,以便定期更新本指南而無需處理合並衝突。


相關文章