Airbnb JavaScript Style 閱讀注解
Airbnb JavaScript Style 閱讀注解
提供一種合理的javascript的規范,對原文主要內容進行翻譯,同時對部分內容進行注釋
注意 :本文假定你正在使用 Babel ,并且要求你使用 babel-preset-airbnb 或者其替代品。同時,假定你已經通過 airbnb-browser-shims 或者其替代品安裝 shims/polyfills 在你的app內。
如果您想了解更多或者在github上觀閱內容:point_down:
Types(數據類型)
-
簡單的基本數據類型,直接使用其值
string
number
boolean
null
undefined
symbol
</pre> </li> </ul>const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
-
復雜的基本數據類型,直接使用其值的引用
object
array
function
</pre> </li> </ul>const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
symbol
- Symbol
symbol 自ES6引入,目的是提供一種機制,保證每個屬性名都是唯一的,從根本上防止屬性名的沖突。在這之前,對象屬性名都是字符串。其實看到這里, string 和 symbol 類型有點 class 和 id 的意思
Symbol() 的聲明,因為 Symbol() 返回值是一個類似于字符串的基本類型,不是一個對象,所以不能使用 new 命令
let ylone = Symbol(); typeof(ylone); :point_down:
"symbol"
//為聲明加上描述 let ylone1 = Symbol('hello'); ylone1; :point_down: Symbol(hello);</pre>
無論是不加描述,還是所加的描述相同, Symbol() 函數的返回值都不相同
Symbol.for('key') 也會返回一個Symbol,但是 Symbol.for() 采用登記機制(會被登記在 全局環境 中供搜索),如果之前 key 已經存在,則直接返回該值,否則新建一個值。比如,如果你調用 Symbol.for("cat") 30 次,每次都會返回同一個Symbol值,但是調用 Symbol("cat") 30 次,會返回 30 個不同的Symbol值。
Symbol 本身不能與其他值進行運算,但是可以轉換成字符串和布爾類型
對象中使用 Symbol() 。通過對比之前通過 a['string'] 的方式,相當于多了一步轉換,來保證屬性命名的安全。
let mySymbol = Symbol(); // 第一種寫法 let a = {}; a[mySymbol] = 'Hello!';
// 第二種寫法 let a = {
[mySymbol]: 'Hello!'
};
// 第三種寫法 let a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' });
a[mySymbol] :point_down: 'hello!'</pre>
注意,由于 . 運算符后面總是字符串,所以 Symbol() 不支持點式聲明對象屬性。在對象內部使用 [symbol] 這樣的寫法也是這個道理
References(引用)
-
聲明創建一個值時用 const 而不用 var ,這樣可以保證你聲明的值不會被重定義
// bad var a = 1; var b = 2;
// good const a = 1; const b = 2;</pre>
-
如果需要改變聲明所創建的值,用 let 而不是 var ,因為 let 是塊級作用域元素, var 是函數作用域元素
// bad var count = 1; if (true) { count += 1; }
// good, use the let. let count = 1; if (true) { count += 1; }</pre> </li> </ul> </li>
-
注意, let 和 const 都是塊級作用域函數,他們都只存在于他們被定義的塊中
// const and let only exist in the blocks they are defined in. { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError
</ul>
- const
const,let,block-scoped,function-scoped
塊級作用域的常量,此聲明創建一個常量,其作用域可以是全局或本地聲明的 塊 。聲明時需要指定其值作為一個常數的初始化器。一般情況下, const 聲明的值不能改變,但是對象元素可以改變其屬性,數組元素可以向其中添加值,但是不能重新賦值
const a = 100; a = 10; :point_right: Uncaught TypeError: Assignment to constant variable
const a = []; a.push('a'); ? a = ['a']; :point_right: Uncaught TypeError: Assignment to constant variable
const obj = {'name':'ylone'}; obj['name'] = 'yh'; ? obj = {'name':'yh'}; :point_right: Uncaught TypeError: Assignment to constant variable</pre>
注意,chrome30嚴格模式下不能使用, const(Uncaught SyntaxError: Use of const in strict mode. )
- let
let允許你聲明一個作用域被限制在塊級中的變量、語句或者表達式。let聲明的變量只在其聲明的塊或子塊中可用,這一點,與var相似。二者之間最主要的區別在于var聲明的變量的作用域是整個封閉函數。
var q = 1; var w = 2; if(true){ var q = 11; let w = 22; console.log(q,w); :point_right:(11,22) } console.log(q,w); :point_right:(11,2)
- block-scoped
在其他類C語言中,由 {} 封閉的代碼塊即為 block-scoped , {..block-scoped..}
if(true){ var a = 100; } a; :point_right: 100
if(true){ let b = 100; } b; :point_right: Uncaught ReferenceError: b is not defined</pre>
如果是類C語言中, a 會在if語句執行完畢后銷毀,但是在javascript中,if中的變量聲明會將變臉那個添加到當前的執行環境中,這里可以看出 var與let的區別 , var 聲明的變量會自動被添加到最接近的執行環境中, let 聲明的變量則只會存在與塊級作用域中
- function-scoped
函數作用域,每個函數被聲明時的上下文執行環境, fucnction(){..function-scoped..}
Objects(對象)
-
直接使用 {} 來創建對象,因為這樣更加簡潔,性能上和 new Object() 也沒差
// bad const item = new Object();
// good const item = {};</pre> </li> </ul>
創建擁有動態屬性名的對象時,用計算機屬性名來表示,這樣可以在創建對象時,將所有的屬性寫在同一個地方
function getKey(k) { return
a key named ${k}
; }// bad const obj = { id: 5, name: 'San Francisco', }; objgetKey('enabled') = true;
// good const obj = { id: 5, name: 'San Francisco',
};</pre>
- 對象屬性中有函數方法時,使用更簡潔的對象字面值方法
// bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, };
// good const atom = { value: 1, addValue(value) { return atom.value + value; }, };</pre>
- 對象屬性和屬性值一致時,使用更簡潔的對象字面值屬性
const lukeSkywalker = 'Luke Skywalker';
// bad const obj = { lukeSkywalker: lukeSkywalker, };
// good const obj = { lukeSkywalker, };</pre>
- 聲明對象時,根據是否使用速記,簡單地對對象的屬性分下類
const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker';
// bad const obj = { episodeOne: 1, twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, };
// good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, };</pre>
-
僅給有特殊符號的標識符提供引號,實際上對象的屬性默認為字符串類型,除非用 [] 標記為符號類型。這樣做的好處在于,增強代碼高亮,方便閱讀,并且對js引擎更加友好
// bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, };
// good const good = { foo: 3, bar: 4, 'data-blah': 5, };</pre> </li>
-
不要直接調用 Object.prototype 下的方法,比如 hasOwnProperty , isPrototypeOf , propertyIsEnumerable 等,因為這些方法可能被覆蓋 { hasOwnProperty: false } ,或者對象為空報錯
// bad console.log(object.hasOwnProperty(key));
// good console.log(Object.prototype.hasOwnProperty.call(object, key));
// best const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. / or / import has from 'has'; // https://www.npmjs.com/package/has // ... console.log(has.call(object, key));</pre> </li>
-
用對象擴散運算符和對象剩余運算符,而不是 Object.assign 來進行淺拷貝操作
// very bad const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // this mutates
original
?_? delete copy.a; // so does this// bad const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// good const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
// noA => { b: 2, c: 3 }</pre> </li> </ul>
call,assign(),...
- call()
Function.prototype.call() ,調用一個函數,其具有指定的 this 值和參數列表。 注意 ,該方法和 apply() 方法類似,區別在于 apply() 傳參為一個包含多個參數的數組。可以讓call()中的對象調用當前對象所擁有的function。
使用 call() 調用父構造函數,在一個子構造函數中,你可以通過調用父構造函數的 call 方法來實現繼承,類似于Java中的寫法
//父構造函數,寫一些公用的方法和屬性 function a(v1,v2){ this.name = v1; this.cool = v2; } //子構造函數,可以繼承父構造函數的方法和屬性,同時可以有私有的方法和屬性 function b(v1,v2,v3){ a.call(this,v1,v2); this.sex = v3; } var v1 = new a('ylone',true); var v2 = new b('ylone',true,'male'); v1; :point_right: {name: "ylone", cool: true} v2; :point_right: {name: "ylone", cool: true, sex: "male"}
使用 call() 調用匿名函數,將參數作為指定的 this值 ,傳進匿名函數。同時也可以傳遞普通參數。
var i = 1; (function(i){console.log(this,i)}).call(Math.random(),i); :point_right: 0.9604319664333041 1
使用 call() 調用函數并且指定執行環境的this
function a(){ console.log(this.name + ' is ' + this.cool); }; var i = {name: 'ylone', cool: 'cool'}; a.call(i); :point_right: ylone is cool
- Object.assign()
和 $.extend() 類似,用于對象的合并,將源對象內所有可枚舉的屬性拷貝到目標對象, 注意 如果源數據不是對象,則先會轉換成對象;如果是 null 或者 undefined 等不能轉換成對象的類型,則根據其位置進行跳過或者報錯。
Object.assign(null); :point_right: Uncaught TypeError: Cannot convert undefined or null to object
Object.assign(1,null); :point_right: Number {1}</pre>
Object.assign() 僅支持淺拷貝,也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用
var v1 = {a:{b:'b'}}; var v2 = Object.assign({},v1); v1.a.b = 'c'; v2.a.b; :point_right: 'c'
Object.assign() 處理數組,會先把數組轉換成對象,將其視為屬性名為 0、1、2 的對象,因此源數組的 0 號屬性4覆蓋了目標數組的 0 號屬性1。
Object.assign([1, 2, 3], [4, 5]); :point_down: Object.assign({0:1,1:2,2:3},{0:4,1:5}); :point_down: {0:4,1:5,2:3} :point_down: [4,5,3]
- ...
對象擴散運算符和對象剩余運算符都用 ... 表示,可以理解為“脫衣服”方法
數組轉換,將數組轉換成逗號分隔的參數序列, 注意 ,其返回值并不是某個基本類型,所以該方法多用于函數參數設置,代替 apply() 方法。對于很多參數不能接受數組的方法提供了便利。
...[1,2,3] :point_right: Uncaught SyntaxError: Unexpected number
[...[1,2,3]] :point_right: [1, 2, 3]
[1,...[2,3],4] :point_right: [1, 2, 3, 4]
//Math.max()不支持數組傳參,之前通過apply()進行轉換 Math.max.apply(null,[1,2,3]) :point_right: 3 //現在可以利用 ... 直接進行轉換 Math.max(...[1,2,3]) :point_right: 3</pre>
Arrays(數組)
-
使用 [] 來創建數組
// bad const items = new Array();
// good const items = [];</pre> </li>
-
使用 push() 而不是直接給數組項賦值
const someStack = [];
// bad someStack[someStack.length] = 'abracadabra';
// good someStack.push('abracadabra');</pre> </li>
-
使用 ... 拷貝數組
// bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; }
// good const itemsCopy = [...items];</pre> </li>
-
使用 ... 將數組對象轉換為數組
const foo = document.querySelectorAll('.foo');
// good const nodes = Array.from(foo);
// best const nodes = [...foo];</pre> </li>
-
用 array.from() 而不是 ... 遍歷迭代器,這樣避免產生了中間變量
// bad const baz = [...foo].map(bar);
// good const baz = Array.from(foo, bar);</pre> </li>
-
數組方法的回調中使用return語句,如果函數體由單語句組成,返回值沒有副作用,return也可以忽略
// good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
// good [1, 2, 3].map(x => x + 1);
// bad - no returned value means
memo
becomes undefined after the first iteration [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memoindex = flatten; });// good [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memoindex = flatten; return flatten; });
// bad inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } else { return false; } });
// good inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; }
return false; });</pre> </li>
-
如果數組有多行,在數組項開始和結束時使用換行符
// bad const arr = [ [0, 1], [2, 3], [4, 5], ];
const objectInArray = [{ id: 1, }, { id: 2, }];
const numberInArray = [ 1, 2, ];
// good const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [ { id: 1, }, { id: 2, }, ];
const numberInArray = [ 1, 2, ];</pre> </li> </ul>
Array.from()
- Array.from()
Array.from() 方法從一個類似數組(一個對象必須有length屬性)或可迭代對象中創建一個新的數組實例,比如 array,map,set,string
//數組 const arr = ['1','2','3']; Array.from(arr); :point_right: ["1", "2", "3"]
//字符串 const str = 'ylone'; Array.from(str); :point_right: ["y", "l", "o", "n", "e"]
//map對象 const m1 = new Map(); m1.set('v1',1); m2.set('v2',2); m2; :point_right: {"v1" => 1, "v2" => 2} Array.from(m2); :point_right: [['v1',1],['v2',2]]
//json對象 const j = {'v1':1,'v2':2}; j.length; :point_right: undefined Array.from(j); :point_right: []</pre>
-
Array.from(arrayLike, mapFn, thisArg)
- arrayLike 表示想要轉換成數組的偽數組對象或可迭代對象
- mapFn(可選參數) 表示新數組中的每個元素會執行該回調函數
-
thisArg(可選參數) 表示執行回調函數 mapFn 時 this 對象
Array.from([1,2,3], function(n){return n+1}) :point_down: [2, 3, 4]
Destructuring(解構)
-
訪問和使用對象的多個屬性時,使用對象解構。這樣可以避免為這些屬性創建臨時引用,保持代碼的整潔。
// bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName;
return
${firstName} ${lastName}
; }// good function getFullName(user) { const { firstName, lastName } = user; return
${firstName} ${lastName}
; }// best function getFullName({ firstName, lastName }) { return
${firstName} ${lastName}
; }</pre> </li> -
使用數組解構
const arr = [1, 2, 3, 4];
// bad const first = arr[0]; const second = arr[1];
// good const [first, second] = arr;</pre> </li>
-
使用對象解構而不是數組解構來實現多個返回值。這樣,您可以添加新的屬性或者更改屬性順序
// bad function processInput(input) { return [left, right, top, bottom]; }
// the caller needs to think about the order of return data const [left, __, top] = processInput(input);
// good function processInput(input) { return { left, right, top, bottom }; }
// the caller selects only the data they need const { left, top } = processInput(input);</pre> </li> </ul>
Destructuring
-
Destructuring :解構。解構的作用是可以快速取得數組或對象當中的元素或屬性,而無需使用arr[x]或者obj[key]等傳統方式進行賦值。
//數組解構 const arr = [1,[2,3],4]; const [a,[b,c],d] = arr; a,b,c,d; :point_right: 1,2,3,4 //函數傳參 var arr = [1, 2, 3]; function fn1([a, b, c]) { return a+b+c; } fn1(arr); :point_right: 6
Strings(字符串)
-
使用單引號 ''
// bad const name = "Capt. Janeway";
// bad - template literals should contain interpolation or newlines const name =
Capt. Janeway
;// good const name = 'Capt. Janeway';</pre> </li>
-
如果字符串很長,不要通過字符串連接符進行換行,保持原來的字符串形式就好。因為破壞字符串是一件很不好的事情,同時也減少了代碼的可讀性
// 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.';</pre> </li>
-
當字符串中有變量時,使用模板字符串而不是連字符。這樣代碼更加簡潔可讀。
// bad function sayHi(name) { return 'How are you, ' + name + '?'; }
// bad function sayHi(name) { return ['How are you, ', name, '?'].join(); }
// bad function sayHi(name) { return
How are you, ${ name }?
; }// good function sayHi(name) { return
How are you, ${name}?
; }</pre> </li> - 不要使用 eval() 方法,因為它有潛在的危險,在不受信任的代碼上使用可以打開一個程序多達幾種不同的注入攻擊。
-
在字符串中不要隨意使用 \ ,因為它影響可讀性,同時可能與轉義符產生火花
// bad const foo = '\'this\' \i\s \"quoted\"';
// good const foo = '\'this\' is "quoted"'; const foo =
my name is '${name}'
;</pre> </li> </ul>Functions(函數)
-
使用命名函數表達式而不是函數聲明。因為如果一個函數聲明被掛起之后,很容易在它被定義之前就去引用,這就很影響代碼的可讀性和可維護性。同時,如果一個函數的功能比較復雜,需要用函數名來對其進行一定的描述
// bad function foo() { // ... }
// bad const foo = function () { // ... };
// good // lexical name distinguished from the variable-referenced invocation(s) const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... };</pre> </li>
-
在 () 創建的函數需要立即調用,自調用函數相當于一個獨立模塊。事實上,IIFE很少在項目中使用
// immediately-invoked function expression (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }());
- 不要在非功能模塊( if , while 等)里面聲明一個函數。將函數分配給一個變量來替代它。因為雖然瀏覽器支持這種做法,但是他們各自的解析方式并不一樣
-
ECMA-262 定義 ‘塊’ 表示一個語句列表,函數聲明并不是一個語句,跟上一點類似
// bad if (currentUser) { function test() { console.log('Nope.'); } }
// good let test; if (currentUser) { test = () => { console.log('Yup.'); }; }</pre> </li>
-
永遠不要給參數命名為 arguments ,這將導致每個函數作用域的 arguments 對象被優先替換
// bad function foo(name, options, arguments) { // ... }
// good function foo(name, options, args) { // ... }</pre> </li>
-
永遠不要使用 arguments ,而使用 ... ,因為 arguments 只是類似數組
// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); }
// good function concatenateAll(...args) { return args.join(''); }</pre> </li>
-
使用函數默認參數語法而不是改變函數的參數
// really bad function handleThings(opts) { // No! We shouldn’t mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {}; // ... }
// still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... }
// good function handleThings(opts = {}) { // ... }</pre> </li>
-
避免函數默認參數使用不當,使用時要考慮場景
var b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3
-
總是將函數默認參數放在傳參的最后
// bad function handleThings(opts = {}, name) { // ... }
// good function handleThings(name, opts = {}) { // ... }</pre> </li>
-
永遠不要使用 Function 構造函數來創建一個新的函數,因為它和 eval() 沆瀣一氣
// bad var add = new Function('a', 'b', 'return a + b');
// still bad var subtract = Function('a', 'b', 'return a - b');</pre> </li>
-
函數簽名的間距,添加或刪除名稱時不需要添加或刪除空格,保持一致性
// bad const f = function(){}; const g = function (){}; const h = function() {};
// good const x = function () {}; const y = function a() {};</pre> </li>
-
不要改變參數,因為操作最為參數傳入的對象可能會改變原對象從而對其他調用產生影響
// bad function f1(obj) { obj.key = 1; }
// good function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; }</pre> </li>
-
不要重新分配參數,特別是在訪問arguments對象時
// bad function f1(a) { a = 1; // ... }
function f2(a) { if (!a) { a = 1; } // ... }
// good function f3(a) { const b = a || 1; // ... }
function f4(a = 1) { // ... }</pre> </li>
-
優先使用 ... 來調用可變參數函數,因為 ... 很干凈,不需要提供上下文環境,并且你不能輕易地使用 apply() 和 new 方法
// bad const x = [1, 2, 3, 4, 5]; console.log.apply(console, x);
// good const x = [1, 2, 3, 4, 5]; console.log(...x);
// bad new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
// good new Date(...[2016, 8, 5]);</pre> </li>
-
使用函數如果有多行簽名或者調用,應該每個 item 單獨放一行,并在最后一項放置一個尾隨逗號
// bad function foo(bar, baz, quux) { // ... }
// good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz);
// good console.log( foo, bar, baz, );</pre> </li> </ul>
Default Function Parameter
- 函數默認參數 ,允許在沒有值或undefined被傳入時使用默認形參
- 函數形式: function(name){param1 = defaultValue1,...,paramN = defaultValueN}
-
JavaScript中函數的參數默認是 undefined
const a = function test(v1,v2=1){ return v1*v2; } a(5,5); :point_right: 25 a(5); :point_right: 5 a(void 0,5); :point_right: NaN
- 可以看出,當設置了函數默認參數后,如果傳參為 undefined ,則會用默認參數替換,否則為原傳參值
-
有默認值的解構函數,通過解構賦值為參數賦值
const b = function test([a,b]=[1,2],{c:c}={c:3}){ return a+b+c; } b(); :point_right: 6 b([2,3],4); :point_right: 9 b(void 0,4); :point_right: 9 b([void 0,3],4); :point_right: NaN
Arrow Functions(箭頭函數)
-
當需要使用一個匿名函數時(比如在傳遞內聯回調時),使用箭頭函數表示
// 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; });</pre> </li> </ul>
如果一個函數的返回值是一個無副作用的單語句,則省略大括號并且隱式返回,否則保留大括號并且使用return聲明
// bad [1, 2, 3].map(number => { const nextNumber = number + 1;
A string containing the ${nextNumber}.
; });// good [1, 2, 3].map(number =>
A string containing the ${number}.
);// good [1, 2, 3].map((number) => { const nextNumber = number + 1; return
A string containing the ${nextNumber}.
; });// good [1, 2, 3].map((number, index) => ({
}));
// No implicit return with side effects function foo(callback) { const val = callback(); if (val === true) { // Do something if callback returns true } }
let bool = false;
// bad foo(() => bool = true);
// good foo(() => { bool = true; });</pre>
-
如果函數表達式有多行,用括號將內容包裹起來,以便更好地閱讀,因為它清除標記了起始和結束位置
// bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) );
// good ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ));</pre> </li>
-
如果函數內始終只有一個參數,則省略括號,否則的話,用括號保護參數
// bad [1, 2, 3].map((x) => x * x);
// good [1, 2, 3].map(x => x * x);
// good [1, 2, 3].map(number => (
A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!
));// bad [1, 2, 3].map(x => { const y = x + 1; return x * y; });
// good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });</pre> </li>
-
避免將箭頭函數語法(=>)與比較運算符(<=,>=)混淆
// bad const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
// bad const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
// good const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
// good const itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height > 256 ? largeSize : smallSize; };</pre> </li> </ul>
arrow Function
- 箭頭函數表達式 的語法比函數表達式更短,并且不綁定自己的this,arguments,super或 new.target。這些函數表達式最適合用于非方法函數,并且它們不能用作構造函數
- const 函數名 = (參數...) => {函數聲明}||表達式
-
執行體為函數聲明時需要加上 {} ,參數的規則參看上文內容
//支持解構函數 const f = ([a,b]=[1,2],{c:c}={c:3})=>a+b+c; f(); :point_right: 6;
Classes & Constructors(類與構造函數)
-
避免直接使用 prototype , 多用 class 。因為 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; } }</pre> </li>
-
使用 extends 實現繼承,因為這是繼承原型的內置功能
// 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]; } }</pre> </li>
-
方法可以通過返回 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() luke.setHeight(20);</pre> </li>
-
寫一個通用的 toString() 方法也沒問題,但是需要保證其能執行且沒有其他影響
class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; }
getName() { return this.name; }
toString() { return
Jedi - ${this.getName()}
; } }</pre> </li> -
如果沒有指定類,那么類需要有一個默認的構造方法。一個空的構造函數或者只是委托給父類是沒有必要的
// bad class Jedi { constructor() {}
getName() { return this.name; } }
// bad class Rey extends Jedi { constructor(...args) { super(...args); } }
// good class Rey extends Jedi { constructor(...args) { super(...args); this.name = 'Rey'; } }</pre> </li>
-
避免出現兩個一樣的類成員,因為前一個成員會被覆蓋從而導致錯誤
// bad class Foo { bar() { return 1; } bar() { return 2; } }
// good class Foo { bar() { return 1; } }
// good class Foo { bar() { return 2; } }</pre> </li> </ul>
Modules(模塊)
-
始終使用模塊( 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;</pre> </li>
-
不要使用通配符進行導出,從而保證你輸出一個獨立的導出
// bad import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good import AirbnbStyleGuide from './AirbnbStyleGuide';</pre> </li>
-
不要把導入和導出寫在一起,雖然一行簡明扼要,但是我們更需要明確的導入方式和導出方式,保持其一致性
// bad // filename es6.js export { es6 as default } from './AirbnbStyleGuide';
// good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;</pre> </li>
-
一個路徑一次支持一個導入,因為一個路徑一次支持有多個導入,會使代碼變得難以維護
// bad import foo from 'foo'; // … some other imports … // import { named1, named2 } from 'foo';
// good import foo, { named1, named2 } from 'foo';
// good import foo, { named1, named2, } from 'foo';</pre> </li>
-
拒絕導出可變綁定,這種方式通常應該避免,但是不排除有某些特殊情況需要這么做,但是應該記住,通常只導出常量引用
// bad let foo = 3; export { foo };
// good const foo = 3; export { foo };</pre> </li>
-
在具有單一導出的模塊中,建議使用默認導出而不是命名導出,這樣對于代碼的可讀性和可維護性更加友好
// bad export function foo() {}
// good export default function foo() {}</pre> </li>
-
把所有的導入語句放在一起
// bad import foo from 'foo'; foo.init();
import bar from 'bar';
// good import foo from 'foo'; import bar from 'bar';
foo.init();</pre> </li>
-
多行導入應該項多行數組和對象一樣縮進,這樣保持 {} 內容的一致性
// bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path';</pre> </li>
-
導出語句中不允許出現 webpack 加載器語法。因為導入中使用加載器語法會將代碼耦合到模塊打包器中,,更建議使用 webpack.config.js
// bad import fooSass from 'css!sass!foo.scss'; import barCss from 'style!css!bar.css';
// good import fooSass from 'foo.scss'; import barCss from 'bar.css';</pre> </li> </ul>
Iterators and Generators(迭代器和發生器)
-
不要使用迭代器,更推薦使用javascript的高階方法而不是 for-in , for-of 這些。使用 map() , every() , filter() , find() , findIndex() , reduce() , some() 等遍歷數組,以及 Object.keys() , Object.values() , Object.entries() 去生成數組,以便迭代對象。因為處理返回值的純函數更容易定位問題
const numbers = [1, 2, 3, 4, 5];
// bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15;
// good let sum = 0; numbers.forEach((num) => { sum += num; }); sum === 15;
// best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15;
// bad const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); }
// good const increasedByOne = []; numbers.forEach((num) => { increasedByOne.push(num + 1); });
// best (keeping it functional) const increasedByOne = numbers.map(num => num + 1);</pre> </li>
- 不要使用發生器,因為他們還沒有很好的兼容
-
如果你一定要用發生器,一定要注意關鍵字符的間距,舉個例子, function* 是一個不同于 function 的獨特構造,并且 * 是其構造的一部分
// bad function * foo() { // ... }
// bad const bar = function * () { // ... };
// bad const baz = function *() { // ... };
// bad const quux = function*() { // ... };
// bad function*foo() { // ... }
// bad function *foo() { // ... }
// very bad function* foo() { // ... }
// very bad const wat = function* () { // ... };
// good function* foo() { // ... }
// good const foo = function* () { // ... };</pre> </li> </ul>
Properties(屬性)
-
通過常量訪問屬性的時候使用 .
const luke = { jedi: true, age: 28, };
// bad const isJedi = luke['jedi'];
// good const isJedi = luke.jedi;</pre> </li>
-
通過變量訪問屬性的時候用 []
const luke = { jedi: true, age: 28, };
function getProp(prop) { return luke[prop]; }
const isJedi = getProp('jedi');</pre> </li>
-
使用 ** 進行指數運算
// bad const binary = Math.pow(2, 10);
// good const binary = 2 ** 10;</pre> </li> </ul>
Variables(變量)
-
總是使用 const 或者 let 來聲明變量,這樣做可以避免污染全局命名空間
// bad superPower = new SuperPower();
// good const superPower = new SuperPower();</pre> </li>
-
每個變量聲明都對應一個 const 或者 let 。這樣做,可以獨立的聲明每一個變量,而不需要考慮 ; 和 , 的關系,同時也方便對每個聲明進行調試,而不是跳過所有的聲明
// 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';</pre> </li>
-
對 let 和 const 進行分組,這樣增強代碼可讀性
// 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;</pre> </li>
-
在需要的地方聲明變量,因為 const 和 let 是塊作用域而不是函數作用域
// bad - unnecessary function call function checkName(hasName) { const name = getName();
if (hasName === 'test') { return false; }
if (name === 'test') { this.setName(''); return false; }
return name; }
// good function checkName(hasName) { if (hasName === 'test') { return false; }
const name = getName();
if (name === 'test') { this.setName(''); return false; }
return name; }</pre> </li>
-
不要進行鏈式聲明變量的操作,這樣可能創建隱式的全局變量
// bad (function example() { // JavaScript interprets this as // let a = ( b = ( c = 1 ) ); // The let keyword only applies to variable a; variables b and c become // global variables. let a = b = c = 1; }());
console.log(a); // throws ReferenceError console.log(b); // 1 console.log(c); // 1
// good (function example() { let a = 1; let b = a; let c = a; }());
console.log(a); // throws ReferenceError console.log(b); // throws ReferenceError console.log(c); // throws ReferenceError
// the same applies for
const
</pre> </li> </ul>不要使用一元遞增和遞減操作符(++,--),因為一元遞增和一元遞減可能受到分號插入的影響,并且可能導致應用中的值遞增或者遞減,并且不會報錯。使用 num += 1 類似的語句也更加有表現力,并且可以避免預先遞增或者遞減從而導致程序發生意外
// bad const array = [1, 2, 3]; let num = 1; num++; --num;
let sum = 0; let truthyCount = 0; for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } }
// good const array = [1, 2, 3]; let num = 1; num += 1; num -= 1;
const sum = array.reduce((a, b) => a + b, 0); const truthyCount = array.filter(Boolean).length; ```</pre>
Hoisting(變量提升)
-
var 聲明被置于函數作用域的頂部,但是他們的賦值不是, const 和 let 聲明會被置于一個新概念 TDZ 內。因此, typeof() 方法不再安全
// we know this wouldn’t work (assuming there // is no notDefined global variable) function example() { console.log(notDefined); // => throws a ReferenceError }
// creating a variable declaration after you // reference the variable will work due to // variable hoisting. Note: the assignment // value of
true
is not hoisted. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; }// the interpreter is hoisting the variable // declaration to the top of the scope, // which means our example could be rewritten as: function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; }
// using const and let function example() { console.log(declaredButNotAssigned); // => throws a ReferenceError console.log(typeof declaredButNotAssigned); // => throws a ReferenceError const declaredButNotAssigned = true; }</pre> </li>
-
匿名函數表達式會提升變量名,而不是函數賦值
function example() { console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () { console.log('anonymous function expression'); }; }</pre> </li>
-
命名函數表達式提升變量名,而不是函數名或者函數體
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'); }; }</pre> </li>
-
函數聲明提升其名字和函數體
function example() { superPower(); // => Flying
function superPower() { console.log('Flying'); } }</pre> </li> </ul>
Comparison Operators & Equality(比較操作符和等號)
- 使用 === , !== 取代 == , !=
-
條件語句比如 if 會強制使用 ToBoolean 抽象方法來進行轉換,并且遵循以下規則:
- Objects 轉換為 true
- Undefined 轉換為 false
- Null 轉換為 false
- Booleans 轉換為 the value of the boolean
- Numbers 轉換為 false 如果是 +0, -0, or NaN , 其余為 true
-
Strings轉換為 false 如果是空字符串 '' , 其余為 true
if ([0] && []) { // true // an array (even an empty one) is an object, objects will evaluate to true }
-
使用布爾值的快捷比較方式,但是顯示比較字符串和數字
// bad if (isValid === true) { // ... }
// good if (isValid) { // ... }
// bad if (name) { // ... }
// good if (name !== '') { // ... }
// bad if (collection.length) { // ... }
// good if (collection.length > 0) { // ... }</pre> </li>
-
在 switch 語句中的 case 和 default 使用 {} 來創建塊,比如 let , const , function , class 也是如此。因為在整個 switch 塊中詞法聲明是隨處可見的,但是只有在賦值時才會被初始化,且只有 case 值達到時才會發生。但是當多個 case 子句試圖定義相同的東西時,就會發生問題
// bad switch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // ... } break; default: class C {} }
// good switch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // ... } break; } case 4: bar(); break; default: { class C {} } }</pre> </li>
-
三元表達式不應該嵌套,而應該單行表達
// bad const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null;
// split into 2 separated ternary expressions const maybeNull = value1 > value2 ? 'baz' : null;
// better const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
// best const foo = maybe1 > maybe2 ? 'bar' : maybeNull;</pre> </li>
-
沒事不要隨便用三元表達式
// bad const foo = a ? a : b; const bar = c ? true : false; const baz = c ? false : true;
// good const foo = a || b; const bar = !!c; const baz = !c;</pre> </li>
-
當多個運算符混在一個語句中時,將需要的運算符括在括號里面,并且用括號區分開 ** , % 與 + , - , * , / ,這樣代碼更加有可讀性,并且澄清了開發者的意圖
// bad const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad const bar = a ** b - 5 % d;
// bad // one may be confused into thinking (a || b) && c if (a || b && c) { return d; }
// good const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
// good const bar = (a ** b) - (5 % d);
// good if (a || (b && c)) { return d; }
// good const bar = a + b / c * d;</pre> </li> </ul>
Blocks(塊)
-
所有的多行塊都要用 {}
// bad if (test) return false;
// good if (test) return false;
// good if (test) { return false; }
// bad function foo() { return false; }
// good function bar() { return false; }</pre> </li>
-
如果使用 if else , else 需要和 if 的 } 在同一行
// bad if (test) { thing1(); thing2(); } else { thing3(); }
// good if (test) { thing1(); thing2(); } else { thing3(); }</pre> </li>
-
如果一個 if else 語句內每個代碼塊都用了 return 語句,那么 else 語句就沒有必要,分成多個 if 語句就行了
// bad function foo() { if (x) { return x; } else { return y; } }
// bad function cats() { if (x) { return x; } else if (y) { return y; } }
// bad function dogs() { if (x) { return x; } else { if (y) { return y; } } }
// good function foo() { if (x) { return x; }
return y; }
// good function cats() { if (x) { return x; }
if (y) { return y; } }
//good function dogs(x) { if (x) { if (z) { return y; } } else { return z; } }</pre> </li> </ul>
Control Statements(控制語句)
-
如果你的控制語句,比如 if , while 等很長,或者超過了行寬,你可以對其中的內容進行換行,但是需要注意,邏輯運算符需要放在行首
// bad if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { thing1(); }
// bad if (foo === 123 && bar === 'abc') { thing1(); }
// bad if (foo === 123 && bar === 'abc') { thing1(); }
// bad if ( foo === 123 && bar === 'abc' ) { thing1(); }
// good if ( foo === 123 && bar === 'abc' ) { thing1(); }
// good if ( (foo === 123 || bar === "abc") && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening() ) { thing1(); }
// good if (foo === 123 && bar === 'abc') { thing1(); }</pre> </li> </ul>
Comments(注釋)
多行注釋使用 /** ... */
// bad // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) {
// ...
return element; }
// good /**
- make() returns a new element
based on the passed-in tag name */ function make(tag) {
// ...
return element; }</pre>
-
單行注釋用 // ,并且在注釋內容的上一行,在注釋語句之前要空一行,當然,如果注釋在文件的第一行就不需要空行了
// bad const active = true; // is current tab
// good // is current tab const active = true;
// bad function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type';
return type; }
// good function getType() { console.log('fetching type...');
// set the default type to 'no type' const type = this.type || 'no type'; return type; }
// also good function getType() { // set the default type to 'no type' const type = this.type || 'no type'; return type; }</pre> </li> </ul>
注釋文字以空格作為開始,方便閱讀
// bad //is current tab const active = true;
// good // is current tab const active = true;
// bad /* make() returns a new element based on the passed-in tag name / function make(tag) {
// ...
return element; }
// good /**
- make() returns a new element
based on the passed-in tag name */ function make(tag) {
// ...
return element; }
為你的提交或者評論加上
FIXME
或者TODO
的前綴,好讓其他開發者迅速明白你的意思。FIXME
表示這個問題需要弄清楚,TODO
表示這個問題需要解決使用
// FIXME
去注釋問題</pre>class Calculator extends Abacus { constructor() { super();
// FIXME: shouldn’t use a global here total = 0; } } ```</pre>
-
使用 // TODO 去注釋問題的解決方法
class Calculator extends Abacus { constructor() { super();
// TODO: total should be configurable by an options param this.total = 0; } }</pre> </li> </ul>
Whitespace(空格)
-
使用 tab 去設置兩個空格
// bad function foo() { ????let name; }
// bad function bar() { ?let name; }
// good function baz() { ??let name; }</pre> </li>
-
-
使用 {} 之前空一格
// 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', });</pre> </li>
-
判斷語句(if,while)左括號之前加一個空格,在函數聲明,函數調用,參數列表的 () 不需要空格
// bad if(isJedi) { fight (); }
// good if (isJedi) { fight(); }
// bad function fight () { console.log ('Swooosh!'); }
// good function fight() { console.log('Swooosh!'); }</pre> </li>
-
操作符之間要加空格
// bad const x=y+5;
// good const x = y + 5;</pre> </li>
-
文件導出通過換行符結束
// bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;
// bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;? ?
// good import { es6 } from './AirbnbStyleGuide'; // ... export default es6;?
-
如果寫一個長的方法鏈(連續使用超過三個方法)時,使用縮進來表示層級關系。使用前導點來表示該行是一個方法調用而不是一個新的語句
// bad $('#items').find('.selected').highlight().end().find('.open').updateCount();
// bad $('#items'). find('.selected'). highlight(). end(). find('.open'). updateCount();
// good $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount();
// bad const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform',
translate(${radius + margin},${radius + margin})
) .call(tron.led);// good const leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform',
translate(${radius + margin},${radius + margin})
) .call(tron.led);// good const leds = stage.selectAll('.led').data(data);</pre> </li>
-
塊與塊,塊與語句之間需要空一行
// bad if (foo) { return bar; } return baz;
// good if (foo) { return bar; }
return baz;
// bad const obj = { foo() { }, bar() { }, }; return obj;
// good const obj = { foo() { },
bar() { }, };
return obj;
// bad const arr = [ function foo() { }, function bar() { }, ]; return arr;
// good const arr = [ function foo() { },
function bar() { }, ];
return arr;</pre> </li>
-
塊內不要空行
// bad function bar() {
console.log(foo);
}
// bad if (baz) {
console.log(qux); } else { console.log(foo);
}
// bad
class Foo {constructor(bar) { this.bar = bar; } }
// good function bar() { console.log(foo); }
// good if (baz) { console.log(qux); } else { console.log(foo); }</pre> </li>
-
() 里面不要加空格
// bad function bar( foo ) { return foo; }
// good function bar(foo) { return foo; }
// bad if ( foo ) { console.log(foo); }
// good if (foo) { console.log(foo); }</pre> </li>
-
[] 不要隨意加空格
// bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]);
// good const foo = [1, 2, 3]; console.log(foo[0]);</pre> </li>
-
{} 里面要加空格
// bad const foo = {clark: 'kent'};
// good const foo = { clark: 'kent' };</pre> </li>
-
除了之前提到的長字符串,避免出現一行代碼超過100個字符的情況,這樣確保了可維護性和可讀性
// bad const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// bad $.ajax({ method: 'POST', url: '
// good const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// good $.ajax({ method: 'POST', url: 'Commas(逗號)
-
逗號不要放在行首
// 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', };</pre> </li> </ul>
有時需要附加的逗號,一是為了在 git 上能保持一致,因為 git 在增減之后都會帶上逗號,二是一些像Babel這樣的轉譯器會自動刪除不必要的逗號,這意味著不必擔心傳統瀏覽器中的逗號尾隨問題
// bad - git diff without trailing comma const hero = { firstName: 'Florence',
- lastName: 'Nightingale'
- lastName: 'Nightingale',
- inventorOf: ['coxcomb chart', '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', ];
// bad function createHero( firstName, lastName, inventorOf ) { // does nothing }
// good function createHero( firstName, lastName, inventorOf, ) { // does nothing }
// good (note that a comma must not appear after a "rest" element) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing }
// bad createHero( firstName, lastName, inventorOf );
// good createHero( firstName, lastName, inventorOf, );
// good (note that a comma must not appear after a "rest" element) createHero( firstName, lastName, inventorOf, ...heroArgs );</pre>
Semicolons(分號)
-
在代碼的結尾一定要用 ; 結尾,防止javascript的自動分號插入機制使整個程序報錯
// bad - raises exception const luke = {} const leia = {} [luke, leia].forEach(jedi => jedi.father = 'vader')
// bad - raises exception const reaction = "No! That's impossible!" (async function meanwhileOnTheFalcon(){ // handle
leia
,lando
,chewie
,r2
,c3p0
// ... }())// bad - returns
undefined
instead of the value on the next line - always happens whenreturn
is on a line by itself because of ASI! function foo() { return 'search your feelings, you know it to be foo' }// good const luke = {}; const leia = {}; [luke, leia].forEach((jedi) => { jedi.father = 'vader'; });
// good const reaction = "No! That's impossible!"; (async function meanwhileOnTheFalcon(){ // handle
leia
,lando
,chewie
,r2
,c3p0
// ... }());// good function foo() { return 'search your feelings, you know it to be foo'; }</pre> </li> </ul>
Type Casting & Coercion(強制類型轉換)
- 在語句開始進行強制類型轉換
-
String 類型
// => this.reviewScore = 9;
// bad const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
// bad const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
// bad const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string
// good const totalScore = String(this.reviewScore);</pre> </li>
-
Number 類型,用 Number 或者 parseInt 進行強制轉換,通常 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);</pre> </li> </ul>
如果 parseInt 是你代碼的瓶頸,你不得不使用移位符來進行轉換時,一定要在注釋里面說明
// good /**
- parseInt was the reason my code was slow.
- Bitshifting the String to coerce it to a
- Number made it a lot faster.
*/
const val = inputValue >> 0;</pre>
-
-
使用移位操作符時需要注意,數字可以表示為64位,但是移位操作符始終返回32位的源,對于大于32位的整數,移位操作可能會導致意外發生。最大的32位支持是 2,147,483,647
2147483647 >> 0; // => 2147483647 2147483648 >> 0; // => -2147483648 2147483649 >> 0; // => -2147483647
-
Booleans 類型
const age = 0;
// bad const hasAge = new Boolean(age);
// good const hasAge = Boolean(age);
// best const hasAge = !!age;</pre> </li> </ul>
Naming Conventions(命名協議)
-
避免使用單字符命名,注意命名描述
// bad function q() { // ... }
// good function query() { // ... }</pre> </li>
-
命名對象,函數和實例時都使用駝峰命名
// bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {}
// good const thisIsMyObject = {}; function thisIsMyFunction() {}</pre> </li>
-
對命名對象和構造函數時使用帕斯卡命名
// 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', });</pre> </li>
-
頭部,尾部不要使用下劃線,因為JavaScript的屬性或者方法沒有隱私的概念。前導下換線是一個常見的慣例,表示“私人”,事實上,這些屬性是完全公開的,這樣會讓人產生誤解
// bad this.firstName = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda';
// good this.firstName = 'Panda';</pre> </li>
-
不要保存 this 指針,使用箭頭函數或者 # 綁定來取代
// 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); }; }</pre> </li>
-
基本文件名應該與其導出名字對應
// file 1 contents class CheckBox { // ... } export default CheckBox;
// file 2 contents export default function fortyTwo() { return 42; }
// file 3 contents export default function insideDirectory() {}
// in some other file // bad import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
// bad import CheckBox from './check_box'; // PascalCase import/export, snake_case filename import forty_two from './forty_two'; // snake_case import/filename, camelCase export import inside_directory from './inside_directory'; // snake_case import, camelCase export import index from './inside_directory/index'; // requiring the index file explicitly import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
// good import CheckBox from './CheckBox'; // PascalCase export/import/filename import fortyTwo from './fortyTwo'; // camelCase export/import/filename import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" // ^ supports both insideDirectory.js and insideDirectory/index.js</pre> </li>
-
默認導出一個方法時,使用駝峰命名表示。同時,你的文件名應該與方法名一致
function makeStyleGuide() { // ... }
export default makeStyleGuide;</pre> </li>
-
導出構造函數,類,單例,函數庫等時,使用帕斯卡命名
const AirbnbStyleGuide = { es6: { }, };
export default AirbnbStyleGuide;</pre> </li>
-
縮略詞應該全是大小字母或者全是小寫字母構成,這樣才有可讀性
// bad import SmsContainer from './containers/SmsContainer';
// bad const HttpRequests = [ // ... ];
// good import SMSContainer from './containers/SMSContainer';
// good const HTTPRequests = [ // ... ];
// also good const httpRequests = [ // ... ];
// best import TextMessageContainer from './containers/TextMessageContainer';
// best const requests = [ // ... ];</pre> </li> </ul>
Accessors(訪問方法)
- 屬性的訪問方法不是必須的
-
不要使用JavaScript的 getters/setters,因為它們會造成意想不到的壞的影響,并且很難去測試,定位。所以如果你要用訪問函數,使用 getVal() 和 setVal() 這樣的方式
// bad class Dragon { get age() { // ... }
set age(value) { // ... } }
// good class Dragon { getAge() { // ... }
setAge(value) { // ... } }</pre> </li>
-
如果一個屬性值或者方法返回值是布爾類型,使用 isVal() 或者 hasVal() 這樣的形式
// bad if (!dragon.age()) { return false; }
// good if (!dragon.hasAge()) { return false; }</pre> </li>
-
可以創建類似 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]; } }</pre> </li> </ul>
Events(事件)
-
當將數據傳遞到事件方法里面的時候,不要使用原始值直接進行傳遞,應該處理成對象字面量。這樣可以方便其他用戶修改或者查看傳遞數據
// bad $(this).trigger('listingUpdated', listing.id); // ... $(this).on('listingUpdated', (e, listingId) => { // do something with listingId });
// good $(this).trigger('listingUpdated', { listingId: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { // do something with data.listingId });</pre> </li> </ul>
jQuery
-
通過 $ 來聲明一個承載jquery的元素
// bad const sidebar = $('.sidebar');
// good const $sidebar = $('.sidebar');
// good const $sidebarBtn = $('.sidebar-btn');</pre> </li>
-
將jquery選擇器緩存起來
// bad function setSidebar() { $('.sidebar').hide();
// ...
$('.sidebar').css({ 'background-color': 'pink', }); }
// good function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide();
// ...
$sidebar.css({ 'background-color': 'pink', }); }</pre> </li>
- 對于 DOM 節點的查詢使用級聯 $('.sidebar ul') 或者 父級 > 子級 $('.sidebar > ul')
-
塊級jQuery對象查詢(通過選擇器對象進行查詢),使用 find
// bad $('ul', '.sidebar').hide();
// bad $('.sidebar').find('ul').hide();
// good $('.sidebar ul').hide();
// good $('.sidebar > ul').hide();
// good $sidebar.find('ul').hide();</pre> </li> </ul>
Standard Library(標準程序庫)
-
使用 Number.isNaN 來代替全局的 isNaN ,因為全局的 isNaN 會強制將非數字類型轉換為數字類型,任何強制轉換為非數字的都會返回true
// bad isNaN('1.2'); // false isNaN('1.2.3'); // true
// good Number.isNaN('1.2.3'); // false Number.isNaN(Number('1.2.3')); // true</pre> </li>
-
使用 Number.isFinite 來代替全局的 isFinite ,因為全局的 isFinite 會強制將非數字類型轉換為數字類型,任何強制轉換為有限數字的結果都會返回true
// bad isFinite('2e3'); // true
// good Number.isFinite('2e3'); // false Number.isFinite(parseInt('2e3', 10)); // true</pre> </li> </ul>
Testing(測試)
- 無論您使用那種框架,都應該測試!
- 盡量去寫一些寫的純函數,并且盡量減少突變情況的發生
- 謹慎使用 stubs(存根) 和 mocks(虛擬數據),他們會讓你的測試更加脆弱
- Airbnb 主要使用 mocha 來進行測試,偶爾也用 tape 來測試小的獨立模塊
- 100%的測試覆蓋率是最理想的
- 每當你修復了一個bug,都需要寫一個回歸測試。未經回歸測試修正的錯誤,未來一定會重現
來自:https://segmentfault.com/a/1190000012875529
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-