1.不要用遍歷器。用JAVAScript高級函數代替`for-in`、 `for-of`。
const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;
// good
let sum = 0;
numbers.forEach(num => sum += num);
sum === 15;
// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
// 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);
2不要直接調用`Object.prototype`上的方法,如`hasOwnProperty`, `propertyIsEnumerable`, `isPrototypeOf`。
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
3.對象淺拷貝時,更推薦使用擴展運算符[就是`...`運算符],而不是[`Object.assign`]
// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// good es6擴展運算符 ...
const original = { a: 1, b: 2 };
// 淺拷貝
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
// rest 賦值運算符
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
4.用擴展運算符做數組淺拷貝,類似上面的對象淺拷貝
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
5. 用 `...` 運算符而不是[`Array.from`]
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
6.用對象的解構賦值來獲取和使用對象某個或多個屬性值。
// 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}`;
}
7. const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
8.多個返回值用對象的解構,而不是數據解構。
// bad
function processInput(input) {
// 然后就是見證奇跡的時刻
return [left, right, top, bottom];
}
// 調用者需要想一想返回值的順序
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// oops, 奇跡又發生了
return { left, right, top, bottom };
}
// 調用者只需要選擇他想用的值就好了
const { left, top } = processInput(input);
9. 用命名函數表達式而不是函數聲明。
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// lexical name distinguished from the variable-referenced invocation(s)
// 函數表達式名和聲明的函數名是不一樣的
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
10.不要使用`arguments`,用rest語法`...`代替。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
11.用`spread`操作符`...`去調用多變的函數更好
// 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]);
12.當你一定要用函數表達式(在回調函數里)的時候就用箭頭表達式吧。
// 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;
});
13.常用`class`,避免直接操作`prototype`
// 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;
}
}
14. 用`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];
}
}
15.可以返回`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);
16.如果沒有具體說明,類有默認的構造方法。一個空的構造函數或只是代表父類的構造函數是不需要寫的。
// 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';
}
}
17. 一個路徑只 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';
18. 做冪運算時用冪操作符 `**` 。
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
19.三元表達式不應該嵌套,通常是單行表達式。
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// better
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
20.避免不需要的三元表達式
// 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;
21. 如果 `if` 語句中總是需要用 `return` 返回, 那后續的 `else` 就不需要寫了。 `if` 塊中包含 `return`, 它后面的 `else if` 塊中也包含了 `return`, 這個時候就可以把 `return` 分到多個 `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;
}
}