1.引用
1.1 使用 const 定义你的所有引用;避免使用 var
为什么? 这样能够确保你不能重新赋值你的引用,否则可能导致错误或者产生难以理解的代码。
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
1.2 如果你必须重新赋值你的引用, 使用 let 代替 var
因为 let 是块级作用域,而 var 是函数作用域。
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
2.对象
2.1使用字面值创建对象。
// bad
const item = new Object();
// good
const item = {};
2.2使用对象方法的简写。
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
2.3使用对象属性值的简写
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
2.4 不要直接调用 Object.prototype 的方法,如:hasOwnProperty, propertyIsEnumerable, 和 isPrototypeOf
为什么? 这些方法可能被以下问题对象的属性追踪 - 相应的有 { hasOwnProperty: false } - 或者,对象是一个空对象 (Object.create(null))。
// 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 */
const has = require('has');
…
console.log(has.call(object, key));
2.5 浅拷贝对象的时候最好是使用 … 操作符而不是 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 }
3.数组
3.1 使用字面值创建数组。eslint: no-array-constructor
// bad
const items = new Array();
// good
const items = [];
3.2 使用拓展运算符 … 复制数组。
// bad
const len = items.length;
const itemsCopy = [];
let i;
for(i = 0;i <len;i++){
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
3.3使用 Array#push 取代直接赋值来给数组添加项。
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
3.4 将一个类数组对象转换成一个数组, 使用展开方法 … 代替 Array.from。
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
3.5 对于对迭代器的映射,使用 Array.from 替代展开方法 … , 因为它避免了创建中间数组。
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
3.6 在数组回调方法中使用 return 语句。 如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值
// bad - 没有返回值,意味着在第一次迭代后 `acc` 没有被定义
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
});
// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[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;
});
4.解构
4.1在访问和使用对象的多个属性的时候使用对象的解构。
为什么? 解构可以避免为这些属性创建临时引用。
// 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}`;
}
4.2 使用数组解构。
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
4.3 对于多个返回值使用对象解构,而不是数组解构。
为什么? 你可以随时添加新的属性或者改变属性的顺序,而不用修改调用方。
// bad
function processInput(input) {
// 处理代码...
return [left, right, top, bottom];
}
// 调用者需要考虑返回数据的顺序。
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// 处理代码...
return { left, right, top, bottom };
}
// 调用者只选择他们需要的数据。
const { left, top } = processInput(input);
5.字符
5.1 使用单引号 ‘’ 定义字符串
// bad
const name = "Capt. Janeway";
// bad - 模板文字应该包含插值或换行。
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';
5.2 使行超过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.';
// 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.';
5.3 当以编程模式构建字符串时,使用字符串模板代替字符串拼接
// 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}?`;
}
5.4 不要在字符串上使用 eval() ,它打开了太多漏洞。
5.5 不要转义字符串中不必要的字符。
为什么? 反斜杠损害了可读性,因此只有在必要的时候才会出现。
// bad
const foo = ‘‘this’ \i\s “quoted”’;
// good
const foo = ‘‘this’ is “quoted”’;
const foo = my name is '${name}'
;
6.方法
6.1 使用命名的函数表达式代替函数声明。
为什么? 函数声明是挂起的,这意味着在它在文件中定义之前,很容易引用函数。这会损害可读性和可维护性。如果您发现函数的定义是大的或复杂的,以至于它干扰了对文件的其余部分的理解,那么也许是时候将它提取到它自己的模块中了!不要忘记显式地命名这个表达式,不管它的名称是否从包含变量(在现代浏览器中经常是这样,或者在使用诸如Babel之类的编译器时)。这消除了对错误的调用堆栈的任何假设。 (Discussion)
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// 从变量引用调用中区分的词汇名称
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
6.2 Wrap立即调用函数表达式
// immediately-invoked function expression (IIFE) 立即调用的函数表达式
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
6.3 切记不要在非功能块中声明函数 (if, while, 等)。 将函数赋值给变量。 浏览器允许你这样做,但是他们都有不同的解释,这是个坏消息。
6.4 永远不要定义一个参数为 arguments。 这将会优先于每个函数给定范围的 arguments 对象。
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
6.5 不要使用 arguments, 选择使用 rest 语法 … 代替
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
6.6 使用默认的参数语法,而不是改变函数参数。
// 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 = {}) {
// ...
}
6.7 总是把默认参数放在最后。
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
6.8 没用变异参数。 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;
}
6.9 不要再赋值参数。
为什么? 重新赋值参数会导致意外的行为,尤其是在访问 arguments 对象的时候。 它还可能导致性能优化问题,尤其是在 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) {
// ...
}
6.10 优先使用扩展运算符 … 来调用可变参数函数。
为什么? 它更加干净,你不需要提供上下文,并且你不能轻易的使用 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]);
7.箭头函数
7.1 当你必须使用匿名函数时 (当传递内联函数时), 使用箭头函数。
为什么? 它创建了一个在 this 上下文中执行的函数版本,它通常是你想要的,并且是一个更简洁的语法。
// 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;
});
7.2 如果函数体包含一个单独的语句,返回一个没有副作用的 expression , 省略括号并使用隐式返回。否则,保留括号并使用 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}.`;
});
8. 类和构造器
8.1 尽量使用 class. 避免直接操作 prototype .
为什么? class 语法更简洁,更容易推理。
// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};
// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
8.2 使用 extends 来扩展继承
8.3 方法返回了 this 来供其内部方法调用
// bad
Jedi.prototype.jump = function () {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function (height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
//可链式调用
luke.jump()
.setHeight(20);
8.4 编写一个自定义的 toString()
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
8.5 如果没有指定类,则类具有默认的构造器。 一个空的构造器或是一个代表父类的函数是没有必要的。
// 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';
}
}
9. 模块
9.1 引入和导出
// 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;
9.2 不要使用通配符导入。
为什么? 这确定你有一个单独的默认导出。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
9.3 不要导出可变的引用
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
9.4 在单个导出的模块中,选择默认模块而不是指定的导出。
为什么? 为了鼓励更多的文件只导出一件东西,这样可读性和可维护性更好。
// bad
export function foo() {}
// good
export default function foo() {}
9.5将所有的 imports 语句放在所有非导入语句的上边。
为什么? 由于所有的 imports 都被提前,保持他们在顶部是为了防止意外发生。
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
10. 迭代器
10.1不要使用迭代器。你应该使用 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;
11 属性
11.1 访问属性时使用点符号。
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
11.2 使用变量访问属性时,使用 []表示法。
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
11.3计算指数时,可以使用 ** 运算符。
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
12 变量
12.1 使用 const 或者 let 来定义变量。 不这样做将创建一个全局变量。 我们希望避免污染全局命名空间。
12.2 不要链式变量赋值。
为什么? 链式变量赋值会创建隐式全局变量。
// bad
(function example() {
// JavaScript 把它解释为
// let a = ( b = ( c = 1 ) );
// let 关键词只适用于变量 a ;变量 b 和变量 c 则变成了全局变量。
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
// 对于 `const` 也一样
12.3 避免使用不必要的递增和递减 (++, --)
// 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;
13 提升
13.1 var 定义的变量会被提升到函数范围的最顶部,但是它的赋值不会。const 和 let 声明的变量受到一个称之为 Temporal Dead Zones (TDZ) 的新概念保护。 知道为什么 typeof 不再安全 是很重要的。
13.2 匿名函数表达式提升变量名,而不是函数赋值。
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}
13.3 函数声明提升其名称和函数体。
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
14 比较运算符和等号
14.1 使用 === 和 !== 而不是 == 和 !=。
14.2 条件语句,例如 if 语句使用 ToBoolean 的抽象方法来计算表达式的结果,并始终遵循以下简单的规则:
Objects 的取值为: true
Undefined 的取值为: false
Null 的取值为: false
Booleans 的取值为: 布尔值的取值
Numbers 的取值为:如果为 +0, -0, or NaN 值为 false 否则为 true
Strings 的取值为: 如果是一个空字符串 ‘’ 值为 false 否则为 true
if ([0] && []) {
// true
// 一个数组(即使是空的)是一个对象,对象的取值为 true
}
14.3 对于布尔值使用简写,但是对于字符串和数字进行显式比较
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
14.4 三目表达式不应该嵌套,通常是单行表达式。
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// 分离为两个三目表达式
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
15.块
15.1 如果你使用的是 if 和 else 的多行代码块,则将 else 语句放在 if 块闭括号同一行的位置。
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
15.2 如果一个 if 块总是执行一个 return 语句,那么接下来的 else 块就没有必要了。 如果一个包含 return 语句的 else if 块,在一个包含了 return 语句的 if 块之后,那么可以拆成多个 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;
}
}
16.注释
16.1 使用 /** … */ 来进行多行注释。
16.2 使用 // 进行单行注释。 将单行注释放在需要注释的行的上方新行。 在注释之前放一个空行,除非它在块的第一行。用一个空格开始所有的注释
17.空格
17.1 使用 tabs (空格字符) 设置为 2 个空格
17.2 在花括号前要放一个空格。
17.3 在控制语句(if、while 等)的小括号前放一个空格。
在函数调用及声明中,不在函数的参数列表前加空格。