参考
airbnb:https://github.com/airbnb/javascript
引用
- 所有的赋值都用
const
,避免使用var
。为什么?因为这个能确保你不会改变你的初始值,重复引用会导致 bug 并且使代码变得难以理解。
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
- 如果你一定要对参数重新赋值,使用
let
,而不是var
为什么?因为
let
是块级作用域,而var
是函数级作用域。
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
[
对象
- 使用字面值创建对象。 ```javascript // bad const item = new Object();
// good const item = {};
- 用对象方法简写。
```javascript
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
// 对象的方法
addValue(value) {
return atom.value + value;
},
};
- 用属性值缩写。
为什么?因为这样写的更少且可读性更高。
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,
};
- 只对那些无效的标示使用引号
''
。为什么?通常我们认为这种方式主观上更易读。不仅优化了代码高亮,而且也更容易被许多 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 }
或空对象Object.create(null)
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // 在模块作用域内做一次缓存。
console.log(has.call(object, key));
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
console.log(has(object, key));
- 对象浅拷贝时,更推荐使用扩展运算符(即
...
运算符),而不是[Object.assign](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
。获取对象指定的几个属性时,用对象的 rest 解构运算符(即...
运算符)更好。
// 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 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 }
[
](#%E7%9B%AE%E5%BD%95)
数组
- 用字面量创建数组。 ```javascript // bad const items = new Array();
// good const items = [];
- 用 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 代替直接向数组中添加一个值。
```javascript
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
- 用扩展运算符做数组浅拷贝,类似上面的对象浅拷贝。
```javascript // 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.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from)` 来将一个可迭代的对象转换成数组。
```javascript
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
[Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from)
将一个类数组对象转成一个数组。
```javascript const arrLike = { 0: ‘foo’, 1: ‘bar’, 2: ‘baz’, length: 3 };
// bad const arr = Array.prototype.slice.call(arrLike);
// good const arr = Array.from(arrLike);
- 用`[Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from)` 而不是 `...` 运算符去做 map 遍历。 因为这样可以避免创建一个临时数组。
```javascript
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
- 4.8 如果一个数组有很多行,在数组的
[
后和]
前断行。请看下面示例:
```javascript // 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, ];
[
](#%E7%9B%AE%E5%BD%95)
<a name="2be143d5"></a>
## 解构
- 用对象的解构赋值来获取和使用对象某个或多个属性值。
> 为什么?解构使您不必为这些属性创建临时引用,并且避免重复引用对象。重复引用对象将造成代码重复、增加阅读次数、提高犯错概率。
```javascript
// 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}`;
}
- 用数组解构。
```javascript const arr = [1, 2, 3, 4];
// bad const first = arr[0]; const second = arr[1];
// good const [first, second] = arr;
- 多个返回值用对象的解构,而不是数组解构。
> 为什么?你可以在后期添加新的属性或者变换变量的顺序而不会破坏原有的引用。
```javascript
// 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);
[
](#%E7%9B%AE%E5%BD%95)
字符串
- 字符串应使用单引号
''
。 ```javascript // bad const name = “Capt. Janeway”;
// bad - 模板字符串应该包含插入文字或换行
const name = Capt. Janeway
;
// good const name = ‘Capt. Janeway’;
- 超过 100 个字符的字符串不应该用字符串连接成多行。
> 为什么?字符串折行增加编写难度且不易被搜索。
```javascript
// 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.';
- 当需要动态生成字符串时,使用模板字符串而不是字符串拼接。
为什么?模板字符串更具可读性、多行语法更简洁以及更方便插入变量到字符串里头。
// 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()
,该方法有太多漏洞。
[
](#%E7%9B%AE%E5%BD%95)
函数
- 使用命名函数表达式而不是函数声明。
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// lexical name distinguished from the variable-referenced invocation(s)
// 函数表达式名和声明的函数名是不一样的
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
- 把立即执行函数包裹在圆括号里。
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
- 注意:ECMA-262 中对块(
block
)的定义是: 一系列的语句。但是函数声明不是一个语句, 函数表达式是一个语句。
```javascript // bad if (currentUser) { function test() { console.log(‘Nope.’); } }
// good let test; if (currentUser) { test = () => { console.log(‘Yup.’); }; }
- 不要用 `arguments` 命名参数。他的优先级高于每个函数作用域自带的 `arguments` 对象,这会导致函数自带的 `arguments` 值被覆盖。
```javascript
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
- 不要使用
arguments
,用收集参数语法...
代替。``为什么?
...
明确你想用哪个参数。而且收集参数是真数组,而不是类似数组的arguments
。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 用默认参数语法而不是在函数里对参数重新赋值。
```javascript // really bad function handleThings(opts) { // 不!我们不该修改 arguments // 第二:如果 opts 的值为 false, 它会被赋值为 {} // 虽然你想这么写,但是这个会带来一些微妙的 bug。 opts = opts || {}; // … }
// still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // … }
// good function handleThings(opts = {}) { // … }
- 避免默认参数的副作用。
> 为什么?他会令人迷惑不解,比如下面这个,a 到底等于几,这个需要想一下。
```javascript
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
- 把默认参数赋值放在最后。 ```javascript // bad function handleThings(opts = {}, name) { // … }
// good function handleThings(name, opts = {}) { // … }
- 不要用函数构造器创建函数。
> 为什么?以这种方式创建函数将类似于字符串 eval(),存在漏洞。
```javascript
// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');
- 不要修改参数.
为什么?操作参数对象对原始调用者会导致意想不到的副作用。就是不要改参数的数据结构,保留参数原始值和数据结构。
// bad
function f1(obj) {
obj.key = 1;
};
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};
- 不要对参数重新赋值
为什么?参数重新赋值会导致意外行为,尤其是对
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) {
// ...
}
[
](#%E7%9B%AE%E5%BD%95)
箭头函数
- 当你一定要用函数表达式(在回调函数里)的时候,使用箭头函数。
为什么?箭头函数中的
this
与定义该函数的上下文中的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;
});
- 如果表达式涉及多行,把他包裹在圆括号里以提高可读性。
为什么?这样能清晰地显示函数的开始位置和结束位置。
// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod
)
);
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod
)
));
- 避免箭头函数(
=>
)和比较操作符(<=
,>=
)``
```javascript // 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; };
[
](#%E7%9B%AE%E5%BD%95)
<a name="5cf3e034"></a>
## 类与构造函数
- 使用 `class` 语法。避免直接操作 `prototype`。
> 为什么?`class` 语法更简洁更易理解。
```javascript
// 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
实现继承。为什么?它是一种内置的方法来继承原型功能而不破坏
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
来实现链式调用。
```javascript // 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);
- 如果没有特别定义,类有默认的构造方法。一个空的构造函数或只是代表父类的构造函数是不需要写的。
```javascript
// 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';
}
}
模块
- 一个路径只
import
一次。为什么?多行导入同一路径将使代码变得难以维护。
// bad
import foo from 'foo';
// … 其他导入 … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
named1,
named2,
} from 'foo';
- 不要导出可变的东西。
为什么?变化通常都是需要避免,特别是当你要输出可变的绑定。虽然在某些场景下可能需要这种技术,但总的来说应该导出常量。
// bad
let foo = 3;
export { foo }
// good
const foo = 3;
export { foo }
- 在一个单一导出模块里,用
export default
更好。为什么?鼓励使用更多文件,每个文件只导出一次,这样可读性和可维护性更好。
// bad
export function foo() {}
// good
export default function foo() {}
- 把
import
放在其他所有语句之前。为什么?因为
import
会被提升到代码最前面运行,因此将他们放在最前面以防止发生意外行为。
// 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';
[
](#%E7%9B%AE%E5%BD%95)
迭代器与生成器
- 不要用迭代器。使用 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);
[
](#%E7%9B%AE%E5%BD%95)
属性
- 访问属性时使用点符号。 ```javascript const luke = { jedi: true, age: 28, };
// bad const isJedi = luke[‘jedi’];
// good const isJedi = luke.jedi;
- 当使用变量获取属性时用方括号 `[]`。
```javascript
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
[
](#%E7%9B%AE%E5%BD%95)
变量
- 使用
const
或let
声明变量。不这样做会导致全局变量。我们想要避免污染全局命名空间。 ```javascript // bad superPower = new SuperPower();
// good const superPower = new SuperPower();
- 为每个变量声明都用一个 `const` 或 `let`。
```javascript
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (与前面的比较,找一找错误)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
- 把
const
和let
分别放一起。为什么?在你需要分配一个新的变量,而这个变量依赖之前分配过的变量的时候,这种做法是有帮助的。
// 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;
- 在你需要的地方声明变量,但是要放在合理的位置。
为什么?
let
和const
都是块级作用域而不是函数级作用域。
// bad - 不必要的函数调用。
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;
}
- 不要使用链式声明变量。 eslint:
[no-multi-assign](https://eslint.org/docs/rules/no-multi-assign)
为什么?链式声明变量会创建隐式全局变量。
// bad
(function example() {
// JavaScript 将这一段解释为
// let a = ( b = ( c = 1 ) );
// let 只对变量 a 起作用; 变量 b 和 c 都变成了全局变量
let a = b = c = 1;
}());
console.log(a); // undefined
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // undefined
console.log(b); // undefined
console.log(c); // undefined
// `const` 也是如此
- 不要使用一元自增自减运算符(
++
,--
).为什么?根据 eslint 文档,一元增量和减量语句受到自动分号插入的影响,并且可能会导致应用程序中的值递增或递减的静默错误。 使用
num + = 1
而不是num ++
或num ++
语句也是含义清晰的。 禁止一元增量和减量语句还会阻止您无意地预增/预减值,这也会导致程序出现意外行为。
// 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;
- 在赋值的时候避免在
=
前/后换行。 如果你的赋值语句超出[max-len](https://eslint.org/docs/rules/max-len.html)
,那就用小括号把这个值包起来再换行。为什么?在
=
附近换行容易混淆这个赋值语句。
// bad
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// bad
const foo
= 'superLongLongLongLongLongLongLongLongString';
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// good
const foo = 'superLongLongLongLongLongLongLongLongString';
- 不允许有未使用的变量。eslint:
[no-unused-vars](https://eslint.org/docs/rules/no-unused-vars)
为什么?一个声明了但未使用的变量更像是由于重构未完成产生的错误。这种在代码中出现的变量会使阅读者迷惑。
// bad
var some_unused_var = 42;
// 写了没用
var y = 10;
y = 5;
// 变量改了自己的值,也没有用这个变量
var z = 0;
z = z + 1;
// 参数定义了但未使用
function getX(x, y) {
return x;
}
// good
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// 'type' 即使没有使用也可以可以被忽略, 因为这个有一个 rest 取值的属性。
// 这是从对象中抽取一个忽略特殊字段的对象的一种形式
var { type, ...coords } = data;
// 'coords' 现在就是一个没有 'type' 属性的 'data' 对象
比较运算符与相等
- 用
===
和!==
而不是==
和!=
- 在
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 {}
}
}
- 三元表达式不应该嵌套,通常是单行表达式。
// 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;
- 避免不必要的三元表达式。 ```javascript // 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;
- 用圆括号来组合操作符。 只有当标准的算术运算符(`+`, `-`, `*`, 和 `/`), 并且它们的优先级显而易见时,才可以不用圆括号括起来。
> 为什么?这提高了可读性,并且明确了开发者的意图。
```javascript
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
// 别人会陷入(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;
[
](#%E7%9B%AE%E5%BD%95)
块
- 如果
if
语句中总是需要用return
返回,那后续的else
就不需要写了。if
块中包含return
, 它后面的else if
块中也包含了return
, 这个时候就可以把return
分到多个if
语句块中。 eslint:[no-else-return](https://eslint.org/docs/rules/no-else-return)
```javascript // 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; } }
[
](#%E7%9B%AE%E5%BD%95)
<a name="ea5302a4"></a>
## 控制语句
- 当你的控制语句(`if`, `while` 等)太长或者超过最大长度限制的时候,把每一个(组)判断条件放在单独一行里。逻辑操作符放在行首。
> 为什么?把逻辑操作符放在行首是让操作符的对齐方式和链式函数保持一致。这提高了可读性,也让复杂逻辑更清晰。
```javascript
// 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();
}
[
](#%E7%9B%AE%E5%BD%95)
注释
多行注释用
/** ... */
。
```javascript // 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; } ```
- 所有注释开头空一格,方便阅读。 ```javascript // 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; } ```
[
](#%E7%9B%AE%E5%BD%95)
空格
- 一个缩进使用两个空格。 ```javascript // bad function foo() { ∙∙∙∙const name; }
// bad function bar() { ∙const name; }
// good function baz() { ∙∙const name; }
- 在大括号前空一格。
```javascript
// 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',
});
- 在控制语句(
if
,while
等)的圆括号前空一格。在函数调用和定义时,参数列表和函数名之间不空格。 ```javascript // bad if(isJedi) { fight (); }
// good if (isJedi) { fight(); }
// bad function fight () { console.log (‘Swooosh!’); }
// good function fight() { console.log(‘Swooosh!’); }
- 用空格来隔开运算符。
```javascript
// bad
const x=y+5;
// good
const x = y + 5;
- 当出现长的方法链式调用时(>2个)用缩进。用点开头强调该行是一个方法调用,而不是一个新的语句。 ```javascript // 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);
- 在一个代码块后下一条语句前空一行。
```javascript
// 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;
不要用空白行填充块。 ```javascript // bad function bar() {
console.log(foo);
}
// also bad if (baz) {
console.log(qux); } else { console.log(foo);
}
// good function bar() { console.log(foo); }
// good if (baz) { console.log(qux); } else { console.log(foo); }
- 不要在代码之间使用多个空白行填充。
```javascript
// bad
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = this.getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
// good
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
- 圆括号里不要加空格。 ```javascript // bad function bar( foo ) { return foo; }
// good function bar(foo) { return foo; }
// bad if ( foo ) { console.log(foo); }
// good if (foo) { console.log(foo); }
- 方括号里不要加空格。
```javascript
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good,逗号分隔符后还是要空格的。
const foo = [1, 2, 3];
console.log(foo[0]);
- 花括号里加空格 。 ```javascript // bad const foo = {clark: ‘kent’};
// good const foo = { clark: ‘kent’ };
- 避免一行代码超过100个字符(包含空格)。注意:对于 [上面](#strings--line-length),长字符串不受此规则限制,不应换行。
> 为什么?这样确保可读性和可维护性。
```javascript
// 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.'));
- 作为语句的花括号内也要加空格 ——
{
后和}
前都需要空格。 ```javascript // bad function foo() {return true;} if (foo) { bar = 0;}
// good function foo() { return true; } if (foo) { bar = 0; }
- `,` 前不要空格, `,` 后需要空格。
```javascript
// bad
var foo = 1,bar = 2;
var arr = [1 , 2];
// good
var foo = 1, bar = 2;
var arr = [1, 2];
- 调用函数时,函数名和小括号之间不要空格。 ```javascript // bad func ();
func ();
// good func();
- 在对象的字面量属性中, `key` 和 `value` 之间要有空格。
```javascript
// bad
var obj = { "foo" : 42 };
var obj2 = { "foo":42 };
// good
var obj = { "foo": 42 };
- 避免出现多个空行。 在文件末尾只允许空一行。避免在文件开始处出现空行。 ```javascript // bad - multiple empty lines var x = 1;
var y = 2;
// bad - 2+ newlines at end of file var x = 1; var y = 2;
// bad - 1+ newline(s) at beginning of file
var x = 1; var y = 2;
// good var x = 1; var y = 2;
[
](#%E7%9B%AE%E5%BD%95)
<a name="c2d5517a"></a>
## 逗号
- 不要前置逗号。
```javascript
// 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',
};
[
](#%E7%9B%AE%E5%BD%95)
分号
- 要分号
为什么?当 JavaScript 遇到没有分号结尾的一行,它会执行 自动插入分号 这一规则来决定行末是否加分号。如果 JavaScript 在你的断行里错误的插入了分号,就会出现一些古怪的行为。当新的功能加到JavaScript 里后, 这些规则会变得更复杂难懂。清晰的结束语句,并通过配置代码检查去检查没有带分号的地方可以帮助你防止这种错误。
// bad - 抛出异常
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => jedi.father = 'vader')
// bad - 抛出异常
const reaction = "No! That’s impossible!"
(async function meanwhileOnTheFalcon() {
// 处理 `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}())
// bad - 将返回 `undefined` 而不是下一行的值。由于 ASI,当 `return`单独出现在一行时,这种情况会一直出现。
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';
}
[
](#%E7%9B%AE%E5%BD%95)
类型转换与强制转换
- 字符串: ```javascript // => this.reviewScore = 9;
// bad const totalScore = new String(this.reviewScore); // typeof totalScore is “object” not “string”
// bad const totalScore = this.reviewScore + ‘’; // 将会执行 this.reviewScore.valueOf()
// bad const totalScore = this.reviewScore.toString(); // 不保证返回 string
// good const totalScore = String(this.reviewScore);
- 数字: 用 `Number` 做类型转换,`parseInt` 转换 `string` 应总是带上基数。
> 为什么?函数 `parseInt` 会根据指定的基数将字符串转换为数字。字符串开头的空白字符将会被忽略,如果参数基数(第二个参数)为 `undefined` 或者 `0` ,除非字符串开头为 `0x` 或 `0X`(十六进制),会默认假设为 `10`。这个差异来自 ECMAScript 3,它不鼓励(但是允许)解释八进制。在 2013 年之前,一些实现不兼容这种行为。因为我们需要支持旧浏览器,所以应当始终指定进制。
```javascript
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);
[
](#%E7%9B%AE%E5%BD%95)
Get-Set 访问器
- 如果属性/方法是
boolean
, 用isVal()
或hasVal()
。
```javascript // bad if (!dragon.age()) { return false; }
// good if (!dragon.hasAge()) { return false; }
- `get()` 和 `set()` 函数是可以的,但是要一起用。
```javascript
class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key];
}
}