禪與JavaScript程式設計藝術,ZenandTheArtofJavaScriptProgramming
禪與 JavaScript 程式設計藝術
Zen and The Art of JavaScript Programming
參考資料: Airbnb JavaScript Style Guide 。
目錄
- 型別
- 引用
- 物件
- 陣列
- 解構
- 字串
- 函式
- 箭頭函式
- 建構函式
- 模組
- 迭代器和生成器
- 屬性
- 變數
- 提升
- 比較運算子和等號
- 程式碼塊
- 註釋
- 空白
- 逗號
- 分號
- 型別轉換
- 命名規則
- 存取器
- 事件
- jQuery
- ECMAScript 5 相容性
- ECMAScript 6 編碼規範
- 測試
- 效能
- 相關資源
- 使用情況
- 其他翻譯
- JavaScript 編碼規範說明
- 討論 JavaScript
- 貢獻者
- 許可協議
型別
-
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 注意
let
和const
都是塊級作用域。// 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 永遠不要在一個非函式程式碼塊(
if
、while
等)中宣告一個函式,把那個函式賦給一個變數。瀏覽器允許你這麼做,但它們的解析表現不一致。 -
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 | MDN 和 ES6 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 將所有的
const
和let
分組為什麼?當你需要把已賦值變數賦值給未賦值變數時非常有用。
// 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 在你需要的地方給變數賦值,但請把它們放在一個合理的位置。
為什麼?
let
和const
是塊級作用域而不是函式作用域。// 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
宣告會被提升至該作用域的頂部,但它們賦值不會提升。let
和const
被賦予了一種稱為「暫時性死區(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 Cherry 的 JavaScript 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 如果通過
if
和else
使用多行程式碼塊,把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 給註釋增加
FIXME
或TODO
的字首可以幫助其他開發者快速瞭解這是一個需要複查的問題,或是給需要實現的功能提供一個解決方式。這將有別於常見的註釋,因為它們是可操作的。使用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 在控制語句(
if
、while
等)的小括號前放一個空格。在函式呼叫及宣告中,不在函式的引數列表前加空格。// 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; })();
型別轉換
-
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 各個特性的列表。
測試
-
28.1 Yup.
function() { return true; }
效能
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Are Javascript functions like
map()
,reduce()
, andfilter()
optimized for traversing arrays? - 等等…
相關資源(英文)
瞭解 ES6
看看這個
工具
- 程式碼風格檢查器(Lint)
其他風格指南
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
其他風格
- Naming this in nested functions – Christian Johansen
- Conditional Callbacks – Ross Allen
- Popular JavaScript Coding Conventions on Github – JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous – Ben Alman
擴充閱讀
- Understanding JavaScript Closures – Angus Croll
- Basic JavaScript for the impatient programmer – Dr. Axel Rauschmayer
- You Might Not Need jQuery – Zack Bloom & Adam Schwartz
- ES6 Features – Luke Hoban
- Frontend Guidelines – Benjamin De Cock
書籍
- JavaScript: The Good Parts – Douglas Crockford
- JavaScript Patterns – Stoyan Stefanov
- Pro JavaScript Design Patterns – Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers – Steve Souders
- Maintainable JavaScript – Nicholas C. Zakas
- JavaScript Web Applications – Alex MacCaw
- Pro JavaScript Techniques – John Resig
- Smashing Node.js: JavaScript Everywhere – Guillermo Rauch
- Secrets of the JavaScript Ninja – John Resig and Bear Bibeault
- Human JavaScript – Henrik Joreteg
- Superhero.js – Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- JSBooks – Julien Bouquillon
- Third Party JavaScript – Ben Vinegar and Anton Kovalyov
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript – David Herman
- Eloquent JavaScript – Marijn Haverbeke
部落格
- DailyJS
- JavaScript Weekly
- JavaScript, JavaScript…
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- Dustin Diaz
- nettuts
播客
使用情況
下列組織應用這份風格指南。
- 3blades: 3Blades/javascript
- 4Catalyzer: 4Catalyzer/javascript
- Aan Zee: AanZee/javascript
- Adult Swim: adult-swim/javascript
- Airbnb: airbnb/javascript
- AltSchool: AltSchool/javascript
- Apartmint: apartmint/javascript
- Ascribe: ascribe/javascript
- Avalara: avalara/javascript
- Avant: avantcredit/javascript
- Axept: axept/javascript
- BashPros: BashPros/javascript
- Billabong: billabong/javascript
- Bisk: bisk/javascript
- Bonhomme: bonhommeparis/javascript
- Brainshark: brainshark/javascript
- CaseNine: CaseNine/javascript
- Chartboost: ChartBoost/javascript-style-guide
- ComparaOnline: comparaonline/javascript
- Compass Learning: compasslearning/javascript-style-guide
- DailyMotion: dailymotion/javascript
- DoSomething: DoSomething/eslint-config
- Digitpaint digitpaint/javascript
- Ecosia: ecosia/javascript
- Evernote: evernote/javascript-style-guide
- Evolution Gaming: evolution-gaming/javascript
- EvozonJs: evozonjs/javascript
- ExactTarget: ExactTarget/javascript
- Expensify Expensify/Style-Guide
- Flexberry: Flexberry/javascript-style-guide
- Gawker Media: gawkermedia/javascript
- General Electric: GeneralElectric/javascript
- Generation Tux: GenerationTux/javascript
- GoodData: gooddata/gdc-js-style
- Grooveshark: grooveshark/javascript
- Honey: honeyscience/javascript
- How About We: howaboutwe/javascript
- Huballin: huballin/javascript
- HubSpot: HubSpot/javascript
- Hyper: hyperoslo/javascript-playbook
- InterCity Group: intercitygroup/javascript-style-guide
- Jam3: Jam3/Javascript-Code-Conventions
- JeopardyBot: kesne/jeopardy-bot
- JSSolutions: JSSolutions/javascript
- KickorStick: kickorstick/javascript
- Kinetica Solutions: kinetica/javascript
- Lonely Planet: lonelyplanet/javascript
- M2GEN: M2GEN/javascript
- Mighty Spring: mightyspring/javascript
- MinnPost: MinnPost/javascript
- MitocGroup: MitocGroup/javascript
- ModCloth: modcloth/javascript
- Money Advice Service: moneyadviceservice/javascript
- Muber: muber/javascript
- National Geographic: natgeo/javascript
- Nimbl3: nimbl3/javascript
- Nulogy: nulogy/javascript
- Orange Hill Development: orangehill/javascript
- Orion Health: orionhealth/javascript
- OutBoxSoft: OutBoxSoft/javascript
- Peerby: Peerby/javascript
- Razorfish: razorfish/javascript-style-guide
- reddit: reddit/styleguide/javascript
- React: facebook.github.io/react/contributing/how-to-contribute.html#style-guide
- REI: reidev/js-style-guide
- Ripple: ripple/javascript-style-guide
- SeekingAlpha: seekingalpha/javascript-style-guide
- Shutterfly: shutterfly/javascript
- Sourcetoad: sourcetoad/javascript
- Springload: springload/javascript
- StratoDem Analytics: stratodem/javascript
- SteelKiwi Development: steelkiwi/javascript
- StudentSphere: studentsphere/javascript
- SwoopApp: swoopapp/javascript
- SysGarage: sysgarage/javascript-style-guide
- Syzygy Warsaw: syzygypl/javascript
- Target: target/javascript
- TheLadders: TheLadders/javascript
- The Nerdery: thenerdery/javascript-standards
- T4R Technology: T4R-Technology/javascript
- VoxFeed: VoxFeed/javascript-style-guide
- WeBox Studio: weboxstudio/javascript
- Weggo: Weggo/javascript
- Zillow: zillow/javascript
- ZocDoc: ZocDoc/javascript
翻譯
這份風格指南也有其他語言的譯本:
-
Brazilian Portuguese: armoucar/javascript-style-guide
-
Bulgarian: borislavvv/javascript
-
Catalan: fpmweb/javascript-style-guide
-
Chinese(Traditional): jigsawye/javascript
- [圖片上傳失敗…(image-3ff722-1529075707265)] Chinese(Simplified): yuche/javascript
-
French: nmussy/javascript-style-guide
-
-
Italian: sinkswim/javascript-style-guide
- [圖片上傳失敗…(image-72093e-1529075707265)] Japanese: mitsuruog/javacript-style-guide
-
Korean: tipjs/javascript-style-guide
-
Polish: mjurczyk/javascript
- [圖片上傳失敗…(image-cfe55e-1529075707265)] Russian: uprock/javascript
-
Spanish: paolocarrasco/javascript-style-guide
-
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.
修訂
我們鼓勵您派生本指南和更改規則以適應您的團隊需求。您可以在下方列出對本風格指南的修改,以便定期更新本指南而無需處理合並衝突。
相關文章
- JavaScript DOM程式設計藝術筆記1JavaScript程式設計筆記
- 《JavaScript Dom程式設計藝術》讀書筆記(一)JavaScript程式設計筆記
- JavaScript DOM 程式設計藝術 學習筆記01JavaScript程式設計筆記
- JavaScript DOM 程式設計藝術 學習筆記 02JavaScript程式設計筆記
- 《Javacript DOM 程式設計藝術》筆記(一)JavaScript Syntax程式設計筆記JavaScript
- JavaScript DOM程式設計藝術筆記2.1準備工作JavaScript程式設計筆記
- 《JavaScript DOM程式設計藝術》作者談:原型程式碼與生產程式碼之間的矛盾JavaScript程式設計原型
- JavaScript DOM 程式設計藝術(第2版) 讀書筆記JavaScript程式設計筆記
- Redux中的程式設計藝術Redux程式設計
- Michael Feathers:程式設計的藝術程式設計
- Java併發程式設計藝術Java程式設計
- JavaScript DOM程式設計藝術第四章 — JavaScript圖片庫案例研究JavaScript程式設計
- 設計的藝術(二):遊戲與遊戲性與互動藝術遊戲
- 推薦JS入門書:JavaScript DOM程式設計藝術(第2版)JSJavaScript程式設計
- Unix哲學(Unix程式設計藝術)程式設計
- 程式設計,不止有程式碼,還有藝術程式設計
- 環境藝術設計 環境藝術設計課程 招聘
- QT QML模組的程式設計藝術QT程式設計
- 《計算機程式設計藝術》作者高德納計算機程式設計
- 《java併發程式設計的藝術》Executor框架Java程式設計框架
- 程式設計的藝術:不巢狀主義程式設計巢狀
- 《java併發程式設計的藝術》原子操作類Java程式設計
- Java併發程式設計的藝術(五)——中斷Java程式設計
- JavaScript非同步程式設計:Generator與AsyncJavaScript非同步程式設計
- R語言程式設計藝術 第2章 向量(上)R語言程式設計
- 《java併發程式設計的藝術》記憶體模型Java程式設計記憶體模型
- 《java併發程式設計的藝術》併發工具類Java程式設計
- 《java併發程式設計的藝術》執行緒池Java程式設計執行緒
- 【讀書筆記】Java併發程式設計的藝術筆記Java程式設計
- Python 物件導向程式設計之封裝的藝術Python物件程式設計封裝
- Java併發程式設計的藝術,解讀併發程式設計的優缺點Java程式設計
- JavaScript函數語言程式設計之pointfree與宣告式程式設計JavaScript函數程式設計
- 淺析 DjangoModel 設計禪道Django
- 與技術無關,但卻值得碼農們好好讀一讀的怪書:禪與摩托車維修藝術
- 《java併發程式設計的藝術》併發容器和框架Java程式設計框架
- 如何評價《Java 併發程式設計藝術》這本書?Java程式設計
- javascript 與 設計模式JavaScript設計模式
- JavaScript物件程式設計JavaScript物件程式設計