訪問此原文地址: http://galaxyteam.pub/didi-fe...
另外歡迎訪問咱們維護的 https://www.threejs.online 中文站 (歡迎Star!)
本文譯者:滴滴出行上海前端(FE)團隊楊永樂同窗javascript
基本類型:直接存取css
string
number
boolean
null
undefined
symbol
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
symbol
類型不能徹底polyfilled,因此請謹慎使用複雜類型: 經過引用的方式存取html
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
使用const
申明引用類型,避免使用var
。eslint 設置:prefer-const,no-const-assign前端
爲何?這能確保你沒法對引用從新賦值,也不會致使出現 bug 或難以理解。
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
若是必須對引用類型從新賦值,使用let
而非var
。eslint設置:no-var jscs: disallowVarjava
爲何?相比於var
函數做用域,let
塊級做用域更容易理解
// bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; }
注意let
和const
都是塊級做用域node
// 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
使用字面值建立對象。eslint: no-new-objectwebpack
// bad const item = new Object(); // good const item = {};
建立對象的動態屬性時,使用計算屬性git
爲何?這樣能夠在一個地方定義對象全部的屬性
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,
[getKey('enabled')]: true, }; ```
使用對象方法的簡寫形式。 eslint: object-shorthand jscs: requireEnhancedObjectLiteralses6
爲何?方法定義簡潔清晰
// 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 jscs: [requireEnhancedObjectLiterals]github
爲何?書寫更加簡潔,更有描述性。
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, };
對象聲明時分類簡寫和非簡寫的屬性名。
爲何?更清晰的瞭解哪些屬性是簡寫的。
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, };
只有對那些不合法的屬性名標識符添加引號。eslint: quote-props jscs: disallowQuotedKeysInObjects
爲何?對象屬性更直觀,可讀性強。可以代碼高亮顯示,同時對於大多數的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
。
爲何?這些方法可能受對象的其餘屬性影響。例如{ hasOwnProperty: false }
或者 對象多是null(Object.create(null)
)
// bad console.log(object.hasOwnProperty(key)); const object = Object.create(null); obj.hasOwnProperty(key) // Uncaught TypeError: obj.hasOwnProperty is not a function // 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));
淺拷貝對象時推薦使用對象展開操做(object spread operator)而不是Object.assign
。使用對象剩餘操做符(object rest operator)獲取對象中剩餘的屬性。
爲何?
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 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
使用字面量聲明數組。eslint: no-array-constructor
// bad const items = new Array(); // good const items = [];
向數組添加元素時,使用Arrary#push
替代直接賦值。
const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra');
使用數組展開操做符...
拷貝數組
// 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.from
const foo = document.querySelectorAll('.foo'); // good const nodes = Array.from(foo); // best const nodes = [...foo];
當須要對可遍歷對象進行map操做時,使用Array.from
而不是展開操做符...
,避免新建一個臨時數組。
// bad const baz = [...foo].map(bar); // good const baz = Array.from(foo, bar);
數組方法回調須要有返回值。若是函數體比較簡單,能夠直接用表達式,省略return
語句。 eslint: array-callback-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); memo[index] = flatten; }); // good [[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; });
若是數組有多行,請在打開和關閉數組括號以前使用換行符
爲何? 更具備可讀性
// 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, ];
訪問和使用對象的多個屬性時用對象解構操做。eslint: prefer-destructuring jscs: requireObjectDestructuring
爲何?解構能夠避免爲這些屬性建立臨時引用。
// 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 jscs: requireArrayDestructuring
const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr;
使用對象解構來實現多個返回值,而不是數組解構。jscs: disallowArrayDestructuringReturn
爲何?你能夠隨時爲返回值新增屬性而不用關心屬性的順序。
// bad function processInput(input) { // then a miracle occurs return [left, right, top, bottom]; } // 調用者須要注意返回值中對象的順序 const [left, __, top] = processInput(input); // good function processInput(input) { // then a miracle occurs return { left, right, top, bottom }; } // 調用者只須要使用它須要的對象 const { left, top } = processInput(input);
字符串使用單引號。eslint: quotes jscs: validateQuoteMarks
// bad const name = "Capt. Janeway"; // bad - 當須要插值或者換行時才使用模板文字 const name = `Capt. Janeway`; // good const name = 'Capt. Janeway';
不超過100個字符的字符串不該該使用鏈接符或者換行書寫。
爲何?換行的字符串很差閱讀,而且不方便搜索代碼。
// 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 template-curly-spacing jscs: requireTemplateStrings
爲何?模板字符串更爲簡潔,更具可讀性。
// 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 不要過多的轉義字符串。eslint: no-useless-escape
爲何?反斜槓影響代碼可讀性,只有在必要的時候才使用。
// bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`;
使用命名函數表達式而不是函數聲明。eslint: func-style jscs: disallowFunctionDeclarations
爲何?函數聲明會被提早。這意味着極可能在函數定義前引用該函數,可是不會報錯。這不利於代碼的可讀性和可維護性。若是你發現一個函數定義的很大很複雜,以致於妨礙了了解文件中的其餘內容,那麼是時候把這個函數提取到本身的模塊中去了!不要忘記顯示指定表達式的名稱,儘管它能從變量名中被推斷出來(現代瀏覽器或者編譯器(如Babel)支持)。這能讓錯誤的調用棧更清晰。( 討論)
// bad function foo() { // ... } // bad const foo = function () { // ... }; // good // 函數名和變量引用名不一樣 const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... };
// Is it worse const sum = function(a, b) { return a + b; }; // than this? const my_sum = function sum(a, b) { return a + b; };
第一個函數沒有.name
屬性,在debugging過程當中,它會是一個匿名函數。第二個函數有名字爲sum
,你能夠檢索到它,調試過程當中可以快速定位。使用banel 和
babel-preset-env
配置,const foo = () => {}
會轉換成var foo = function foo () {}
,而且從Node v6開始,const foo = () => {}
中的foo 也有.name
。因此它再也不是匿名函數。(函數名字推斷)
用圓括號包裹當即執行函數表達式(IIFE)。eslint: wrap-iife jscs: requireParenthesesAroundIIFE
爲何? 當即執行函數表達式是單一執行單元-使用圓括號包裹調用,簡潔明瞭的表示了這一點。請注意,在通用的模塊中,你幾乎用不到IIFE。
// immediately-invoked function expression (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }());
注意:ECMA-262把block
定義爲一組語句。可是函數聲明不是語句。
// bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
永遠不要把參數命名爲arguments
。這將取代原來函數做用域內的 arguments
對象。
// bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... }
不要使用arguments
。能夠選擇 rest 語法 ...
替代。
爲何?使用...
能明確你要傳入的參數。另外 rest 參數是一個真正的數組,而arguments
是一個類數組。
// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); }
使用函數默認參數指定默認值,而不是用一個可變的函數參數
// really bad function handleThings(opts) { // 不!咱們不該該改變函數參數 // 更糟糕的是: 若是 opts 是 falsy (爲''或者是false), 它仍然會被賦值爲對象,可是這可能會引起bug opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... }
8.使用函數參數默認值的時避免反作用。
> 爲何?這樣的寫法會讓人困惑。 ```javascript 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 = {}) { // ... }
不要使用Function
構造器建立函數。 eslint: no-new-func
爲何?經過這種方式建立的函數和使用
eval()
相似,會帶來不肯定的問題
// bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b');
函數名兩邊留白。eslint: space-before-function-paren [space-before-blocks]
爲何?保持代碼一致性,當你添加或者刪除名字時不須要額外增減空格。
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {};
不要修改參數。 eslint: no-param-reassign
爲何?操做參數對象會在原始調用方中致使不可預知的變量反作用。
// 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
爲何?從新分配參數可能會致使意外的行爲,特別是在訪問參數對象時。 它也可能致使優化問題,特別是在V8中。
// 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
爲何?它更簡潔,你不須要提供上下文,而且組合使用new
和apply
不容易。
// 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]);
帶有多行函數簽名或調用的函數應該像本指南中的其餘多行列表同樣縮進:每行中包含一項,最後一個項目帶有逗號。
// bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, );
當你必需要使用匿名函數(如在傳遞內聯回調時),請使用箭頭函數。eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions
爲何?由於箭頭函數創造了新的一個 this 執行環境,一般狀況下都能知足你的需求,並且這樣的寫法更爲簡潔。(參考 Arrow functions - JavaScript | MDN )爲何不?若是你有一個至關複雜的函數,你或許能夠把邏輯部分轉移到一個函數聲明上。
// 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, arrow-body-style jscs: disallowParenthesesAroundArrowParam, requireShorthandArrowFunctions
爲何?這是一個很好用的語法糖。在鏈式調用中可讀性很高。
// 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
[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; }); ```
若是表達式過長鬚要多行表示,請將其包含在括號中,增長可讀性。
爲何?它能清除的標識函數的開始和結束位置。
// 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的 「always」 option 或者不要在jscs中引入 disallowParenthesesAroundArrowParam。eslint: arrow-parens jscs: disallowParenthesesAroundArrowParam
爲何? 不那麼混亂,可讀性強。
// 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; });
避免箭頭函數語法(=>
)和比較運算符(<=
,=>
)一塊兒使用時帶來的困惑。
// 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; };
老是使用class
。避免直接操做prototype
。
爲何?
class
語法更簡潔更易於理解。
// bad function Queue(contents = []) { this.queue = [...contents]; } Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value; }; // good class Queue { constructor(contents = []) { this.queue = [...contents]; } pop() { const value = this.queue[0]; this.queue.splice(0, 1); return value; } }
使用extends
繼承。
爲何? 由於 extends 是一個內建的原型繼承方法而且不會破壞 instanceof。
// bad const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); } inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; }; // good class PeekableQueue extends Queue { peek() { return this.queue[0]; } }
方法能夠返回 this 來幫助鏈式調用。
// bad Jedi.prototype.jump = function () { this.jumping = true; return true; }; Jedi.prototype.setHeight = function (height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // good class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump() .setHeight(20);
能夠寫一個自定義的 toString() 方法,但要確保它能正常運行而且不會引發反作用。
class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } }
類有默認構造器。一個空的構造函數或者只是重載父類構造函數是沒必要要的。eslint: no-useless-constructor
// 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
爲何?重複的類成員聲明中只有最後一個生效-重複的聲明確定是一個錯誤。
// bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } }
老是使用模組 (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;
不要使用通配符 import
爲何?這樣確保只有一個默認的export
// bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide';
不要直接從import
中export
爲何?雖然一行代碼簡潔明瞭,但讓import
和export
各司其職讓事情能保持一致。
// bad // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;
同一個路徑只使用一次import
。eslint: no-duplicate-imports
爲何?相同路徑有多個
import
會致使代碼難以維護。
// 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
爲何?避免不肯定的可變量,特別是export
可變的綁定。若是某些特殊狀況須要使用這種場景,一般應該export
常量引用。
// bad let foo = 3; export { foo }; // good const foo = 3; export { foo };
模塊中只有單個export
,最好使用default export
。 eslint: import/prefer-default-export
爲何?一個文件最好只作一件事,這樣更具有可讀性和可維護性。
// bad export function foo() {} // good export default function foo() {}
將全部的import
語句放在文件的頂部。eslint: import/first
爲何?因爲
import
s會被提高,最好保持它們在頂部以防出現不可預期的行爲。
// bad import foo from 'foo'; foo.init(); import bar from 'bar'; // good import foo from 'foo'; import bar from 'bar'; foo.init();
多行import
應該和多行數組和對象同樣有縮進。
爲何?花括號須要遵循與指南中的每一個其餘花括號相同的縮進規則,末尾的逗號也同樣。
// bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path';
禁止在模塊導入語句中使用Webpack加載器語法。eslint: import/no-webpack-loader-syntax
爲何?在import
中使用webpack 語法會將代碼耦合進bundler中。推薦在webpack.config.js
中配置loader 規則。
// 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';