1. 程式人生 > >Airbnb JavaScript程式碼規範(完整)

Airbnb JavaScript程式碼規範(完整)

### 型別Types 基本資料型別 - string - number - boolean - null - undefined - symbol ```js const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9 ``` - Symbols不能真正的被polyfilled,因此當目標瀏覽器或者環境本地不支援時,不應當使用Symbols. 複雜資料型別 - object - array - function ```js const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9 ``` ### 引用References - 儘量使用const,避免使用var。eslint: [prefer-const](https://link.jianshu.com?t=http://eslint.org/docs/rules/prefer-const.html),[no-const-assign](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-const-assign.html) ```js // bad var a = 1; var b = 2; // good const a = 1; const b = 2; ``` - 如果你必須對變數重新賦值,使用let. eslint: [no-var](https://link.jianshu.com?t=http://eslint.org/docs/rules/novar.html) > why? let屬於塊作用域,var是函式作用域 ```js // bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; } ``` - 記住let和const都屬於塊作用域 ```js // 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 ``` ### 物件Objects - 使用字面量方式建立物件。eslint: [no-new-object](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-new-object.html) ```js // bad const item = new Object(); // good const item = {}; ``` - 在建立物件時,定義物件的所有屬性 > Why?這樣物件所有的屬性都在同一處定義 ```js 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, }; ``` - 使用簡寫方式定義物件方法。eslint: [object-shorthand](https://link.jianshu.com?t=http://eslint.org/docs/rules/object-shorthand.html) ```js // bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, }; ``` - 使用簡寫方式定義物件屬性。eslint: [object-shorthand ](https://link.jianshu.com?t=http://eslint.org/docs/rules/object-shorthand.html) > why?書寫更簡潔 ```js const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, }; ``` - 把簡寫的屬性放在物件定義的開頭 > why?這樣更容易判斷哪些屬性使用了簡寫 ```js 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, }; ``` - 只對不合法的標誌符使用引號 > why?通常這樣程式碼更可讀,同時語法高亮,且js引擎更容易優化。 ```js // bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, }; ``` - 不要直接呼叫Object.prototype,同理: hasOwnProperty, propertyIsEnumerable, isPrototypeOf > why?這些方法可能被吞沒,比如用Object.create(null)方式建立的物件 ```js // 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'; // ... console.log(has.call(object, key)); ``` - 使用物件的spread操作符而不是Object.assign方法來淺拷貝物件。使用rest操作符獲得一個去除某些屬性新的物件。 ```js // 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 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 } ``` ### 陣列Arrays - 使用字面量定義陣列 eslint: [no-array-constructor](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-array-constructor.html) ```js // bad const items = new Array(); // good const items = []; ``` - 使用Array#push方法新增元素 ```js const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra'); ``` - 使用...操作拷貝陣列 ```js // bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]; ``` - 轉換array-like物件為array時,使用...而不是Array.from ```js const foo = document.querySelectorAll('.foo'); // good const nodes = Array.from(foo); // best const nodes = [...foo]; ``` - 對陣列進行map時,使用Array.from替代...。因為前者的方式能避免建立中間陣列 ```js // bad const baz = [...foo].map(bar); // good const baz = Array.from(foo, bar); ``` - 在陣列遍歷處理的回撥函式中,使用返回語句。當回撥函式中函式體只有單條語句且不會產生副作用時,可以省略return。eslint: [array-callback-return](https://link.jianshu.com?t=http://eslint.org/docs/rules/array-callback-return) ```js // 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 const flat = {}; [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memo[index] = flatten; }); // good const flat = {}; [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memo[index] = 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; }); ``` - 當陣列有多行時,在開始和結束符號均換行 ```js // 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, ]; ``` ### 解構Destructuring - 當訪問物件的多個屬性時,使用解構方式 eslint: [prefer-destructuring](https://link.jianshu.com?t=https://eslint.org/docs/rules/prefer-destructuring) > why?解構可以減少臨時變數的定義 ```js // 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}`; } ``` - 使用陣列解構 eslint: [prefer-destructuring](https://link.jianshu.com?t=https://eslint.org/docs/rules/prefer-destructuring) ```js const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr; ``` - 當返回多個值時,使用物件解構方式,而不是陣列解構 > why?當新增欄位或者順序發生變化時,不依賴於位置 ```js // bad function processInput(input) { // then a miracle occurs 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) { // then a miracle occurs return { left, right, top, bottom }; } // the caller selects only the data they need const { left, top } = processInput(input); ``` ### 字串Strings - 字串使用單引號' ' eslint: [quotes](https://link.jianshu.com?t=http://eslint.org/docs/rules/quotes.html) ```js // bad const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines const name = `Capt. Janeway`; // good const name = 'Capt. Janeway'; ``` - 字串超過100個字元時,不應該跨行 > 斷開的字串不易搜尋,且不方便 ```js // 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.'; ``` - 拼接字串時,使用模板字串 eslint: [prefer-template](https://link.jianshu.com?t=http://eslint.org/docs/rules/prefer-template.html) [template-curly-spacing](https://link.jianshu.com?t=http://eslint.org/docs/rules/template-curly-spacing) > why?模板字串可讀性更強,語法更簡潔 ```js // 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}?`; } ``` - 永遠不要使用eval()字串 eslint: [no-eval](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-eval) - 不要多餘的轉義 eslint: [no-useless-escape](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-useless-escape) > why?影響可讀性 ```js // bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`; ``` ### 函式Functions - ?使用命名函式表示式,而不是函式宣告 eslint: [func-style](https://link.jianshu.com?t=http://eslint.org/docs/rules/func-style) > 函式宣告的方式存在提升,即:無論在哪裡宣告,效果等同於在函式頂部宣告,只要在同一個作用域範圍,就視為已經宣告,哪怕在宣告前就使用,也不會報錯。 > 如果使用函式宣告方式定義函式,會影響可讀性和可維護性。當函式足夠大或者複雜時,對閱讀其餘程式碼造成困擾。 ```js // bad function foo() { // ... } // bad const foo = function () { // ... }; // good // lexical name distinguished from the variable-referenced invocation(s) const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... }; ``` - 要求 IIFE 使用括號括起來 eslint: [wrap-iife](https://link.jianshu.com?t=http://eslint.org/docs/rules/wrap-iife.html) ```js // immediately-invoked function expression (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }()); ``` - ?Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint: [no-loop-func ](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-loop-func.html) - ? **Note:** ECMA-262 defines a block as a list of statements. A function declaration is not a statement. [Read ECMA-262’s note on this issue](https://link.jianshu.com?t=http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). ```js // bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; } ``` - 不要命名引數為arguments。 ```js // bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... } ``` - 選擇預設引數的方式,避免修改引數 ```js // 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 = {}) { // ... } ``` - 避免預設引數帶來的副作用 ```js var b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3 ``` - 將預設引數放置最後 ```js // bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... } ``` - 不要使用Function建構函式建立函式 eslint: [no-new-func](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-new-func) ```js // bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b'); ``` - 函式簽名留空格 [space-before-function-paren](https://link.jianshu.com?t=http://eslint.org/docs/rules/space-before-function-paren) [space-before-blocks](https://link.jianshu.com?t=http://eslint.org/docs/rules/space-before-blocks) ```js // bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {}; ``` - 不要修改引數eslint: [no-param-reassign](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-param-reassign.html) > why?修改作為引數傳入的物件,容易引發不預料的副作用 ```js // bad function f1(obj) { obj.key = 1; } // good function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; } ``` - 不要對引數重新賦值 eslint: [no-param-reassign](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-param-reassign.html) > why?對引數重新賦值會引發不可預料的行為,特別是訪問arguments物件時。同時,會引發V8優化問題 ```js // 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) { // ... } ``` - 使用...操作符呼叫變長函式 eslint: [prefer-spread](https://link.jianshu.com?t=http://eslint.org/docs/rules/prefer-spread) > why?更簡潔 ```js // 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]); ``` - 函式簽名或者函式呼叫佔多行時,每一個引數佔一行,並且最後一個引數帶逗號結束 ```js // bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, ); ``` ### 箭頭函式Arrow Functions - 如果必須使用匿名函式,或者inline 回撥函式,使用箭頭函式。eslint: [prefer-arrow-callback](https://link.jianshu.com?t=http://eslint.org/docs/rules/prefer-arrow-callback.html), [arrow-spacing](https://link.jianshu.com?t=http://eslint.org/docs/rules/arrow-spacing.html) > why?語法更簡潔,並且this更符合預期 > 如果函式邏輯相當複雜,應當使用命名函式 ```js // 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; }); ``` - 如果函式體只有一條語句,且該語句不會產生副作用。使用簡寫方式,隱式返回;或者使用完整寫法,顯式return。 eslint: [arrow-parens](https://link.jianshu.com?t=http://eslint.org/docs/rules/arrow-parens.html), [arrow-body-style](https://link.jianshu.com?t=http://eslint.org/docs/rules/arrow-body-style.html) ```jvascript // 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) => ({ [index]: number, })); // 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; }); ``` - 當表示式佔多行時,使用括號括起來增強可讀性 > why?函式開頭和結束更明確 ```js // bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // good ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) )); ``` - 如果函式只有一個引數,省略括號,省略花括號。否則,一直使用完整寫法,保持一致性。eslint: [arrow-parens](https://link.jianshu.com?t=http://eslint.org/docs/rules/arrow-parens.html) ```js // 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; }); ``` - 使用無歧義的=>語法,與<=,>=區分開。eslint: [no-confusing-arrow](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-confusing-arrow) ```js // 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; ``` ### 類和建構函式 Classes & Constructors - 使用class,避免直接操作prototype > why?class 語法更簡潔,更易懂 ```js // 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; } } ``` - 使用extends 繼承 > why?這是內建支援的繼承原型方法,同時不影響instanceof結果 ```js // 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]; } } ``` - 通過return this幫助鏈式方法呼叫 ```js // 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); ``` - 自定義toString方法 ```js class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } } ``` - class有預設建構函式,因此沒必要定義空的建構函式。eslint: [no-useless-constructor](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-useless-constructor) ```js // 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'; } } ``` - 避免重複的類成員定義 eslint: [no-dupe-class-members](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-dupe-class-members) > why?後者會靜默覆蓋前者 ```js // bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } } ``` ### 模組Modules - 使用modules(import/export) > why?modules是未來的趨勢 ```js // 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; ``` - 不要使用*import ```js // bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide'; ``` - 不要從import直接export > why?儘管單行的方式更簡潔,但是一行import一行export程式碼一致性更好 ```js // bad // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6; ``` - 只從一個地方import eslint: [no-duplicate-imports](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-duplicate-imports) > why?維護性更好 ```js // 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'; ``` - 不要export變值 eslint: [import/no-mutable-exports](https://link.jianshu.com?t=https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) ```js // bad let foo = 3; export { foo }; // good const foo = 3; export { foo }; ``` - 模組只有單個export時,傾向使用export default eslint: [import/prefer-default-export ](https://link.jianshu.com?t=https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) > why?可讀性和可維護性更強 ```js // bad export function foo() {} // good export default function foo() {} ``` - 把import語句置頂 eslint: [import/first](https://link.jianshu.com?t=https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md) > why?因為import行為會提升,因此保持它們放在最上面,防止產生令人奇怪的行為 ```js // bad import foo from 'foo'; foo.init(); import bar from 'bar'; // good import foo from 'foo'; import bar from 'bar'; foo.init(); ``` - 多行import,像多行陣列和多行物件字面量一樣寫 > why?縮排一致,尾行逗號結束 ```js // bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path'; ``` - 在import語句時不要使用webpack loader語法。eslint: [import/no-webpack-loader-syntax](https://link.jianshu.com?t=https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) ```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'; ``` ### 迭代器與生成器 Iterators and Generators - 不要使用迭代器。更傾向於使用JavaScript的高階函式代替迴圈(如for-in 或者for-of)eslint: [no-iterator](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-iterator.html) [no-restricted-syntax ](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-restricted-syntax) > why?有利於函數語言程式設計 > 使用map() / every() / filter() / find() / findIndex() / reduce() / some() / ... 迭代陣列, Object.keys() / Object.values() / Object.entries() 生成陣列,這樣做到可以迭代任何物件 ```js 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); ``` - 暫時不要使用生成器 > why?es5編譯得不太好 - 如果你必須使用生成器,確保函式簽名空格方式正確。eslint: [generator-star-spacing](https://link.jianshu.com?t=http://eslint.org/docs/rules/generator-star-spacing) > why? function和*都是相同概念級別的關鍵字的一部分。function*是唯一的識別符號,區別於function。 ```js // 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* () { // ... }; ``` ### 屬性Properties - 不要使用引號方式訪問屬性 eslint: [dot-notation](https://link.jianshu.com?t=http://eslint.org/docs/rules/dot-notation.html) ```js const luke = { jedi: true, age: 28, }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi; ``` - 通過變數訪問物件屬性時,使用[] ```js const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi'); ``` - 使用**操作符進行指數計算 eslint: [no-restricted-properties](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-restricted-properties). ```js // bad const binary = Math.pow(2, 10); // good const binary = 2 ** 10; ``` ### 變數 - 總是使用const或者let定義變數。避免汙染全域性名稱空間。eslint: [no-undef ](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-undef) [prefer-const](https://link.jianshu.com?t=http://eslint.org/docs/rules/prefer-const) ```js // bad superPower = new SuperPower(); // good const superPower = new SuperPower(); ``` - 一個const或者let定義一個變數 eslint: [one-var](https://link.jianshu.com?t=http://eslint.org/docs/rules/one-var.html) > why? 更方便新增變數定義,方便debug ```js // 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'; ``` - const放在一起,let放在一起 > why?更容易區分 ```js // 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; ``` - 需要時再定義變數,並且放在合理的地方 > why?let和const屬於塊作用域 ```js // 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; } ``` - 不要巢狀變數賦值 > why?巢狀變數賦值隱式建立全域性變數 ```js // 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` ``` - 禁用一元操作符 ++ 和 -- eslint [no-plusplus](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-plusplus) > why?因為一元操作符 ++ 和 -- 會自動新增分號,不同的空白可能會改變原始碼的語義。 ```js // 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; ``` ### 提升Hoisting - var宣告變數會提升到作用域頂部,賦值操作不會提升。const和let沒有類似的提升行為,因此要明白typeof操作不再安全。 ```js // 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; } ``` - 匿名函式表示式會提升變數,但是不會提升函式賦值 ```js function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { console.log('anonymous function expression'); }; } ``` - 命名函式表示式會提升變數,但是不是函式名或者函式體 ```js 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'); }; } ``` - 函式定義提升函式名稱和函式體 ```js function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } } ``` ### 比較操作符和等於操作Comparison Operators & Equality - 使用===、!== eslint: [eqeqeq](https://link.jianshu.com?t=http://eslint.org/docs/rules/eqeqeq.html) - 條件語句比如if,判斷表示式值時,會強制執行ToBoolean,遵循以下規則: 1. Object:true 2. Undefined:false 3. Null:false 4. Booleans 取決於本身的值 5. Number:+0,-0,NaN為false,其餘為true 6. String:‘’空字串為false,其餘為true ```js if ([0] && []) { // true // an array (even an empty one) is an object, objects will evaluate to true } ``` - 使用簡寫方式判斷,但是對strings和numbers比較使用顯式判斷 ```js // bad if (isValid === true) { // ... } // good if (isValid) { // ... } // bad if (name) { // ... } // good if (name !== '') { // ... } // bad if (collection.length) { // ... } // good if (collection.length > 0) { // ... } ``` - 使用{}在case、default子句中定義變數、函式和類 eslint: [no-case-declarations](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-case-declarations.html) ```js // 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 {} } } ``` - 三元操作放在一行 eslint: [no-nested-ternary](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-nested-ternary.html) ```js // 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; ``` - 避免不必要的三元操作 eslint: [no-unneeded-ternary](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-unneeded-ternary.html) ```js // 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; ``` - 禁止混合使用不同的操作符 eslint: [no-mixed-operators](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-mixed-operators.html) ```js // bad const foo = a && b < 0 || c > 0 || d + 1 === 0; // bad const bar = a ** b - 5 % d; // bad 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; ``` ### 塊Blocks - ?使用{} ```js // 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; } ``` - 把else與if的結束放在一行 eslint: [brace-style](https://link.jianshu.com?t=http://eslint.org/docs/rules/brace-style.html) ```js // bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); } ``` - 禁止 if 語句中 return 語句之後有 else 塊 eslint: [no-else-return](https://link.jianshu.com?t=https://eslint.org/docs/rules/no-else-return) ```js // 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; } } return z; } ``` ### 控制語句 Control Statements - 對於控制語句中的條件表示式太長的情況,應當換行處理,把操作符放在每行的開頭。 ```js // 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(); } ``` ### 註釋 - 使用/** ... */多行註釋 ```js // 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; } ``` - 單行註釋使用//。//放在程式碼的上面一行,同時,如果不是區塊的開頭,註釋上方空行處理。 ```js // 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; } ``` - 所有註釋空格開頭,增強易讀性 eslint: [spaced-comment](https://link.jianshu.com?t=http://eslint.org/docs/rules/spaced-comment) ```js // 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 註釋問題 ```js class Calculator extends Abacus { constructor() { super(); // FIXME: shouldn’t use a global here total = 0; } } ``` - 使用 // TODO 註釋問題的解決方案 ```js class Calculator extends Abacus { constructor() { super(); // TODO: total should be configurable by an options param this.total = 0; } } ``` ### 空白Whitespace eslint: [indent](https://link.jianshu.com?t=http://eslint.org/docs/rules/indent.html) - 使用2個空格 ```js // bad function foo() { ∙∙∙∙let name; } // bad function bar() { ∙let name; } // good function baz() { ∙∙let name; } ``` - 在 { 之前空格 eslint: [space-before-blocks](https://link.jianshu.com?t=http://eslint.org/docs/rules/space-before-blocks.html) ```js // 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', }); ``` - 在條件判斷語句的 ( 之前空格。 eslint: [keyword-spacing](https://link.jianshu.com?t=http://eslint.org/docs/rules/keyword-spacing.html) ```js // bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ('Swooosh!'); } // good function fight() { console.log('Swooosh!'); } ``` - 要求操作符周圍有空格 eslint: [space-infix-ops](https://link.jianshu.com?t=http://eslint.org/docs/rules/space-infix-ops.html) ```js // bad const x=y+5; // good const x = y + 5; ``` - 檔案結束時換一行 . eslint: [eol-last](https://link.jianshu.com?t=https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) ```js // bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6; // bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ↵ // good import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ``` - 要求方法鏈中每個呼叫都有一個換行符 eslint: [newline-per-chained-call](https://link.jianshu.com?t=http://eslint.org/docs/rules/newline-per-chained-call) ```js // 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); ``` - 在每個程式碼塊的結束位置和下一語句的開頭空行 ```js // 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; ``` - 不要使用空行填充程式碼塊 eslint: [padded-blocks](https://link.jianshu.com?t=http://eslint.org/docs/rules/padded-blocks.html) ```js // 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); } ``` - 禁止在 () 內有空格 eslint: [space-in-parens](https://link.jianshu.com?t=http://eslint.org/docs/rules/space-in-parens.html) ```js // bad function bar( foo ) { return foo; } // good function bar(foo) { return foo; } // bad if ( foo ) { console.log(foo); } // good if (foo) { console.log(foo); } ``` - 禁止在 [] 內使用空格 eslint: [array-bracket-spacing](https://link.jianshu.com?t=http://eslint.org/docs/rules/array-bracket-spacing.html) ```js // bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good const foo = [1, 2, 3]; console.log(foo[0]); ``` - 在 {} 中使用空格 eslint: [object-curly-spacing](https://link.jianshu.com?t=http://eslint.org/docs/rules/object-curly-spacing.html) ```js // bad const foo = {clark: 'kent'}; // good const foo = { clark: 'kent' }; ``` - 強制一行的最大長度100 eslint: [max-len](https://link.jianshu.com?t=http://eslint.org/docs/rules/max-len.html) ```js // 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: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // 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: 'https://airbnb.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.')); ``` - 逗號風格:結束位置 eslint: [comma-style](https://link.jianshu.com?t=http://eslint.org/docs/rules/comma-style.html) ```js // 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', }; ``` - 末尾逗號 eslint: [comma-dangle](https://link.jianshu.com?t=http://eslint.org/docs/rules/comma-dangle.html) > why?減少無意義的git diffs ```js // 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'], }; ``` ```js // 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 ); ``` ### 分號 Semicolons - eslint: [semi](https://link.jianshu.com?t=http://eslint.org/docs/rules/semi.html) ```js // bad (function () { const name = 'Skywalker' return name })() // good (function () { const name = 'Skywalker'; return name; }()); // good, but legacy (guards against the function becoming an argument when two files with IIFEs are concatenated) ;((() => { const name = 'Skywalker'; return name; })()); ``` ### 型別轉換和強制型別轉換 - ?Perform type coercion at the beginning of the statement. - Strings: ```js // => this.reviewScore = 9; // 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); ``` - Numbers:使用Number和parseInt eslint: [radix](https://link.jianshu.com?t=http://eslint.org/docs/rules/radix) ```js 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); ``` - 不管因為什麼原因你必須使用位移操作,增加註釋說明 ```js // 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; ``` - 小心位移操作。數值能表示64位數,但是位移操作只能返回32位整數。 ```js 2147483647 >> 0; // => 2147483647 2147483648 >> 0; // => -2147483648 2147483649 >> 0; // => -2147483647 ``` - Boolean ```js const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // best const hasAge = !!age; ``` ### 命名約定Naming Conventions - 不要使用一個字母命名 eslint: [id-length](https://link.jianshu.com?t=http://eslint.org/docs/rules/id-length) ```js // bad function q() { // ... } // good function query() { // ... } ``` - 使用駝峰命名物件、函式和例項 eslint: [camelcase](https://link.jianshu.com?t=http://eslint.org/docs/rules/camelcase.html) ```js // bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {} ``` - 使用PascalCase命名建構函式或者Class eslint: [new-cap](https://link.jianshu.com?t=http://eslint.org/docs/rules/new-cap.html) ```js // 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', }); ``` - 不要使用 _ 開頭或者結束 eslint: [no-underscore-dangle](https://link.jianshu.com?t=http://eslint.org/docs/rules/no-underscore-dangle.html) > why?JavaScript沒有私有屬性或者私有方法的概念。使用下劃線容易造成誤解。 ```js // bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // good this.firstName = 'Panda'; ``` - 不要儲存this的引用。使用箭頭函式或者 Function#bind ```js // 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); }; } ``` - 檔名稱與export default 相符 ```js // 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 ``` - 當export default一個函式時,使用駝峰命名。檔名也必須一致 ```js function makeStyleGuide() { // ... } export default makeStyleGuide; ``` - 當export constructor / class / singleton / function library / bare object,使用PascalCase命名方式 ```js const AirbnbStyleGuide = { es6: { }, }; export default AirbnbStyleGuide; ``` - 縮略詞必須首字母大寫,或者全部小寫 ```js // 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 = [ // ... ]; ``` ### 訪問器Accessors - 不需要屬性的訪問器函式 - 不要使用getters/setters,會產生不可預期的副作用,而且難以維護和測試。 ```js // bad class Dragon { get age() { // ... } set age(value) { // ... } } // good class Dragon { getAge() { // ... } setAge(value) { // ... } } ``` - 如果屬性或者方法是boolean,使用 isVal() 或者 hasVal() ```js // bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; } ``` - 建立 get() 和 set() 方法沒有問題,但是保持一致性 ```js class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } } ``` ### 事件Events - 對於event的handler傳遞資料時,使用 { key:value... } 方式。這樣,當需要傳遞更多資訊時,不需要更改每個handler簽名 ```js // 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 }); ``` ### jQuery - jQuery物件變數使用 $ 區分 ```js // bad const sidebar = $('.sidebar'); // good const $sidebar = $('.sidebar'); // good const $sidebarBtn = $('.sidebar-btn'); ``` - 快取jQuery lookups ```js // bad function setSidebar() { $('.sidebar').hide(); // ... $('.sidebar').css({ 'background-color': 'pink', }); } // good function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ... $sidebar.css({ 'background-color': 'pink', }); } ``` - Dom查詢,使用級聯 $('.sidebar ul') 或者父子 $('.sidebar > ul') - 指定範圍進行find ```js // bad $('ul', '.sidebar').hide(); // bad $('.sidebar').find('ul').hide(); // good $('.sidebar ul').hide(); // good $('.sidebar > ul').hide(); // good $sidebar.find('ul').hide