Airbnb JavaScript Style Guide

Airbnb JavaScript Style Guide()

用更合理的方式寫 JavaScript

類型

  • [1.1]基本類型: 直接存取基本類型。
    • 字符串
    • 數值
    • 布爾類型
    • 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

引用

  • 2.1 <a name='2.1'></a> 對所有的引用使用 const ;不要使用 var。 爲什麼?這能確保你無法對引用重新賦值,也不會導致出現 bug 或難以理解。 // bad var a = 1; var b = 2; // good const a = 1; const b = 2;
  • 2.2 <a name='2.2'></a> 如果你一定需要可變動的引用,使用 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 <a name='2.3'></a> 注意 letconst 都是塊級作用域。 // const 和 let 只存在於它們被定義的區塊內。 { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError

對象

  • 3.1 <a name='3.1'></a> 使用字面值創建對象。 // bad const item = new Object(); // good const item = {};
  • 3.2 <a name='3.2'></a> 如果你的代碼在瀏覽器環境下執行,別使用 保留字 作爲鍵值。這樣的話在 IE8 不會運行。 更多信息。 但在 ES6 模塊和服務器端中使用沒有問題。 // bad const superman = { default: { clark: 'kent' }, private: true, }; // good const superman = { defaults: { clark: 'kent' }, hidden: true, };
  • 3.3 <a name='3.3'></a> 使用同義詞替換需要使用的保留字。 // bad const superman = { class: 'alien', }; // bad const superman = { klass: 'alien', }; // good const superman = { type: 'alien', };

<a name="es6-computed-properties"></a>

  • 3.4 <a name='3.4'></a> 創建有動態屬性名的對象時,使用可被計算的屬性名稱。 爲什麼?因爲這樣可以讓你在一個地方定義所有的對象屬性。 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, };

<a name="es6-object-shorthand"></a>

  • 3.5 <a name='3.5'></a> 使用對象方法的簡寫。 // bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, };

<a name="es6-object-concise"></a>

  • 3.6 <a name='3.6'></a> 使用對象屬性值的簡寫。 爲什麼?因爲這樣更短更有描述性。 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 <a name='4.1'></a> 使用字面值創建數組。 // bad const items = new Array(); // good const items = [];
  • 4.2 <a name='4.2'></a> 向數組添加元素時使用 Arrary#push 替代直接賦值。 const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra');

<a name="es6-array-spreads"></a>

  • 4.3 <a name='4.3'></a> 使用拓展運算符 ... 複製數組。 // 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 <a name='5.1'></a> 使用解構存取和使用多屬性對象。 爲什麼?因爲解構能減少臨時引用屬性。 // 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 <a name='5.2'></a> 對數組使用解構賦值。 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);

⬆ 返回目錄

<a name="strings"></a>

Strings

  • 6.1 <a name='6.1'></a> 字符串使用單引號 '' 。 // bad const name = "Capt. Janeway"; // good const name = 'Capt. Janeway';
  • 6.2 <a name='6.2'></a> 字符串超過 80 個字節應該使用字符串連接號換行。
  • 6.3 <a name='6.3'></a> 注:過度使用字串連接符號可能會對性能造成影響。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.';

<a name="es6-template-literals"></a>

  • 6.4 <a name='6.4'></a> 程序化生成字符串時,使用模板字符串代替字符串連接。 爲什麼?模板字符串更爲簡潔,更具可讀性。 // 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}?`; }

⬆ 返回目錄

<a name="functions"></a>

函數

  • 7.1 <a name='7.1'></a> 使用函數聲明代替函數表達式。 爲什麼?因爲函數聲明是可命名的,所以他們在調用棧中更容易被識別。此外,函數聲明會把整個函數提升(hoisted),而函數表達式只會把函數的引用變量名提升。這條規則使得箭頭函數可以取代函數表達式。 // bad const foo = function () { }; // good function foo() { }
  • 7.2 <a name='7.2'></a> 函數表達式: // 立即調用的函數表達式 (IIFE) (() => { console.log('Welcome to the Internet. Please follow me.'); })();
  • 7.3 <a name='7.3'></a> 永遠不要在一個非函數代碼塊(ifwhile 等)中聲明一個函數,把那個函數賦給一個變量。瀏覽器允許你這麼做,但它們的解析表現不一致。
  • 7.4 <a name='7.4'></a> 注意: ECMA-262 把 block 定義爲一組語句。函數聲明不是語句。閱讀 ECMA-262 關於這個問題的說明。 // bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
  • 7.5 <a name='7.5'></a> 永遠不要把參數命名爲 arguments。這將取代原來函數作用域內的 arguments 對象。 // bad function nope(name, options, arguments) { // ...stuff... } // good function yup(name, options, args) { // ...stuff... }

<a name="es6-rest"></a>

  • 7.6 <a name='7.6'></a> 不要使用 arguments。可以選擇 rest 語法 ... 替代。 爲什麼?使用 ... 能明確你要傳入的參數。另外 rest 參數是一個真正的數組,而 arguments 是一個類數組。 // bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); }

<a name="es6-default-parameters"></a>

  • 7.7 <a name='7.7'></a> 直接給函數的參數指定默認值,不要使用一個變化的函數參數。 // 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 <a name='7.8'></a> 直接給函數參數賦值時需要避免副作用。

爲什麼?因爲這樣的寫法讓人感到很困惑。

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

⬆ 返回目錄

<a name="arrow-functions"></a>

箭頭函數

  • 8.1 <a name='8.1'></a> 當你必須使用函數表達式(或傳遞一個匿名函數)時,使用箭頭函數符號。 爲什麼?因爲箭頭函數創造了新的一個 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 <a name='8.2'></a> 如果一個函數適合用一行寫出並且只有一個參數,那就把花括號、圓括號和 return 都省略掉。如果不是,那就不要省略。 爲什麼?語法糖。在鏈式調用中可讀性很高。 爲什麼不?當你打算回傳一個對象的時候。 // good [1, 2, 3].map(x => x * x); // good [1, 2, 3].reduce((total, n) => { return total + n; }, 0);

⬆ 返回目錄

<a name="constructors"></a>

構造器

  • 9.1 <a name='9.1'></a> 總是使用 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 <a name='9.2'></a> 使用 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 <a name='9.3'></a> 方法可以返回 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 <a name='9.4'></a> 可以寫一個自定義的 toString() 方法,但要確保它能正常運行並且不會引起副作用。 class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } }

⬆ 返回目錄

<a name="modules"></a>

模塊

  • 10.1 <a name='10.1'></a> 總是使用模組 (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 <a name='10.2'></a> 不要使用通配符 import。 爲什麼?這樣能確保你只有一個默認 export。 // bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 10.3 <a name='10.3'></a>不要從 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;

⬆ 返回目錄

<a name="iterators-and-generators"></a>

Iterators and Generators

  • 11.1 <a name='11.1'></a> 不要使用 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 <a name='11.2'></a> 現在還不要使用 generators。

爲什麼?因爲它們現在還沒法很好地編譯到 ES5。 (譯者注:目前(2016/03) Chrome 和 Node.js 的穩定版本都已支持 generators)

⬆ 返回目錄

<a name="properties"></a>

屬性

  • 12.1 <a name='12.1'></a> 使用 . 來訪問對象的屬性。 const luke = { jedi: true, age: 28, }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi;
  • 12.2 <a name='12.2'></a> 當通過變量訪問屬性時使用中括號 []。 const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi');

⬆ 返回目錄

<a name="variables"></a>

變量

  • 13.1 <a name='13.1'></a> 一直使用 const 來聲明變量,如果不這樣做就會產生全局變量。我們需要避免全局命名空間的污染。地球隊長已經警告過我們了。(譯註:全局,global 亦有全球的意思。地球隊長的責任是保衛地球環境,所以他警告我們不要造成「全球」污染。) // bad superPower = new SuperPower(); // good const superPower = new SuperPower();
  • 13.2 <a name='13.2'></a> 使用 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 <a name='13.3'></a> 將所有的 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 <a name='13.4'></a> 在你需要的地方給變量賦值,但請把它們放在一個合理的位置。 爲什麼?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; }

⬆ 返回目錄

<a name="hoisting"></a>

Hoisting

  • 14.1 <a name='14.1'></a> 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 <a name='14.2'></a> 匿名函數表達式的變量名會被提升,但函數內容並不會。 function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function() { console.log('anonymous function expression'); }; }
  • 14.3 <a name='14.3'></a> 命名的函數表達式的變量名會被提升,但函數名和函數函數內容並不會。 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 <a name='14.4'></a> 函數聲明的名稱和函數體都會被提升。 function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
  • 想了解更多信息,參考 Ben CherryJavaScript Scoping & Hoisting

⬆ 返回目錄

<a name="comparison-operators--equality"></a>

比較運算符和等號

  • 15.1 <a name='15.1'></a> 優先使用 ===!== 而不是 ==!=.
  • 15.2 <a name='15.2'></a> 條件表達式例如 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 <a name='15.3'></a> 使用簡寫。 // bad if (name !== '') { // ...stuff... } // good if (name) { // ...stuff... } // bad if (collection.length > 0) { // ...stuff... } // good if (collection.length) { // ...stuff... }
  • 15.4 <a name='15.4'></a> 想了解更多信息,參考 Angus Croll 的 Truth Equality and JavaScript

⬆ 返回目錄

<a name="blocks"></a>

代碼塊

  • 16.1 <a name='16.1'></a> 使用大括號包裹所有的多行代碼塊。 // 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 <a name='16.2'></a> 如果通過 ifelse 使用多行代碼塊,把 else 放在 if 代碼塊關閉括號的同一行。 // bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); }

⬆ 返回目錄

<a name="comments"></a>

註釋

  • 17.1 <a name='17.1'></a> 使用 /** ... */ 作爲多行註釋。包含描述、指定所有參數和返回值的類型和值。 // 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 <a name='17.2'></a> 使用 // 作爲單行註釋。在評論對象上面另起一行使用單行註釋。在註釋前插入空行。 // 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 <a name='17.3'></a> 給註釋增加 FIXMETODO 的前綴可以幫助其他開發者快速瞭解這是一個需要複查的問題,或是給需要實現的功能提供一個解決方式。這將有別於常見的註釋,因爲它們是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement
  • 17.4 <a name='17.4'></a> 使用 // FIXME: 標註問題。 class Calculator { constructor() { // FIXME: shouldn't use a global here total = 0; } }
  • 17.5 <a name='17.5'></a> 使用 // TODO: 標註問題的解決方式。 class Calculator { constructor() { // TODO: total should be configurable by an options param this.total = 0; } }

⬆ 返回目錄

<a name="whitespace"></a>

空白

  • 18.1 <a name='18.1'></a> 使用 2 個空格作爲縮進。 // bad function() { ∙∙∙∙const name; } // bad function() { ∙const name; } // good function() { ∙∙const name; }
  • 18.2 <a name='18.2'></a> 在花括號前放一個空格。 // 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 <a name='18.3'></a> 在控制語句(ifwhile 等)的小括號前放一個空格。在函數調用及聲明中,不在函數的參數列表前加空格。 // bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ('Swooosh!'); } // good function fight() { console.log('Swooosh!'); }
  • 18.4 <a name='18.4'></a> 使用空格把運算符隔開。 // bad const x=y+5; // good const x = y + 5;
  • 18.5 <a name='18.5'></a> 在文件末尾插入一個空行。 // bad (function(global) { // ...stuff... })(this); // bad (function(global) { // ...stuff... })(this);↵ ↵ // good (function(global) { // ...stuff... })(this);↵
  • 18.5 <a name='18.5'></a> 在使用長方法鏈時進行縮進。使用前面的點 . 強調這是方法調用而不是新語句。 // 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 <a name='18.6'></a> 在塊末和新語句前插入空行。 // 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;

⬆ 返回目錄

<a name="commas"></a>

逗號

  • 19.1 <a name='19.1'></a> 行首逗號:不需要。 // 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 <a name='19.2'></a> 增加結尾的逗號: 需要。 爲什麼? 這會讓 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', ];

⬆ 返回目錄

<a name="semicolons"></a>

分號

  • 20.1 <a name='20.1'></a> 使用分號 // bad (function() { const name = 'Skywalker' return name })() // good (() => { const name = 'Skywalker'; return name; })(); // good (防止函數在兩個 IIFE 合併時被當成一個參數) ;(() => { const name = 'Skywalker'; return name; })(); Read more.

⬆ 返回目錄

<a name="type-casting--coercion"></a>

類型轉換

  • 21.1 <a name='21.1'></a> 在語句開始時執行類型轉換。
  • 21.2 <a name='21.2'></a> 字符串: // => this.reviewScore = 9; // bad const totalScore = this.reviewScore + ''; // good const totalScore = String(this.reviewScore);
  • 21.3 <a name='21.3'></a> 對數字使用 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 <a name='21.4'></a> 如果因爲某些原因 parseInt 成爲你所做的事的瓶頸而需要使用位操作解決性能問題時,留個註釋說清楚原因和你的目的。 // good /** * 使用 parseInt 導致我的程序變慢, * 改成使用位操作轉換數字快多了。 */ const val = inputValue >> 0;
  • 21.5 <a name='21.5'></a> 注: 小心使用位操作運算符。數字會被當成 64 位值,但是位操作運算符總是返回 32 位的整數(參考)。位操作處理大於 32 位的整數值時還會導致意料之外的行爲。關於這個問題的討論。最大的 32 位整數是 2,147,483,647: 2147483647 >> 0 //=> 2147483647 2147483648 >> 0 //=> -2147483648 2147483649 >> 0 //=> -2147483647
  • 21.6 <a name='21.6'></a> 布爾: const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // good const hasAge = !!age;

⬆ 返回目錄

<a name="naming-conventions"></a>

命名規則

  • 22.1 <a name='22.1'></a> 避免單字母命名。命名應具備描述性。 // bad function q() { // ...stuff... } // good function query() { // ..stuff.. }
  • 22.2 <a name='22.2'></a> 使用駝峯式命名對象、函數和實例。 // bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {}
  • 22.3 <a name='22.3'></a> 使用帕斯卡式命名構造函數或類。 // 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 <a name='22.4'></a> 不要使用下劃線 _ 結尾或開頭來命名屬性和方法。 // bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // good this.firstName = 'Panda';
  • 22.5 <a name='22.5'></a> 別保存 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 <a name='22.6'></a> 如果你的文件只輸出一個類,那你的文件名必須和類名完全保持一致。 // 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 <a name='22.7'></a> 當你導出默認的函數時使用駝峯式命名。你的文件名必須和函數名完全保持一致。 function makeStyleGuide() { } export default makeStyleGuide;
  • 22.8 <a name='22.8'></a> 當你導出單例、函數庫、空對象時使用帕斯卡式命名。 const AirbnbStyleGuide = { es6: { } }; export default AirbnbStyleGuide;

⬆ 返回目錄

<a name="accessors"></a>

存取器

  • 23.1 <a name='23.1'></a> 屬性的存取函數不是必須的。
  • 23.2 <a name='23.2'></a> 如果你需要存取函數時使用 getVal()setVal('hello')。 // bad dragon.age(); // good dragon.getAge(); // bad dragon.age(25); // good dragon.setAge(25);
  • 23.3 <a name='23.3'></a> 如果屬性是布爾值,使用 isVal()hasVal()。 // bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; }
  • 23.4 <a name='23.4'></a> 創建 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]; } }

⬆ 返回目錄

<a name="events"></a>

事件

  • 24.1 <a name='24.1'></a> 當給事件附加數據時(無論是 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 <a name='25.1'></a> 使用 $ 作爲存儲 jQuery 對象的變量名前綴。 // bad const sidebar = $('.sidebar'); // good const $sidebar = $('.sidebar');
  • 25.2 <a name='25.2'></a> 緩存 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 <a name='25.3'></a> 對 DOM 查詢使用層疊 $('.sidebar ul') 或 父元素 > 子元素 $('.sidebar > ul')jsPerf
  • 25.4 <a name='25.4'></a> 對有作用域的 jQuery 對象查詢使用 find。 // bad $('ul', '.sidebar').hide(); // bad $('.sidebar').find('ul').hide(); // good $('.sidebar ul').hide(); // good $('.sidebar > ul').hide(); // good $sidebar.find('ul').hide();

⬆ 返回目錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章