Airbnb es6 编码风格翻译
Airbnb JavaScript 的编码风格 原文地址: Airbnb/JavaScript
前言
翻译 Airbnb JavaScript Style Guide 主要是为了学习 JavaScript
编码风格。因为不是专业的译者,翻译不足之处请指出。
Airbnb JavaScript Style Guide
合理地编写 JavaScript
其他的风格指导
类型
1.1 基本类型: 基本类型直接获取它的值
string
number
boolean
null
undefined
1
2
3
4
5
6const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 91.2 复杂类型: 复杂类型通过引用获取值
object
array
function
1
2
3
4
5
6const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
引用
2.1 请使用
const
修饰所有引用而不是var
。eslint:prefer-const
,no-const-assign
以此确保你不能对引用重新赋值,避免难以理解的代码和 Bug 。
1
2
3
4
5
6
7// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;2.2 如果非要对引用重新复制,请使用
let
而不是var
。eslint:no-var
jscs:disallowVar
原因是
let
为块级作用域,而var
是 函数作用域。1
2
3
4
5
6
7
8
9
10
11// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}2.3 注意
let
和const
都是块级作用域。1
2
3
4
5
6
7// const 和 let 只存在其定义的区块中。
{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
对象
3.1 请使用 literal syntax 创建对象. eslint:
no-new-object
1
2
3
4
5// bad
const item = new Object();
// good
const item = {};3.2 如果代码在浏览器的脚本环境中执行,请勿使用[保留字]当键值,IE8中会失效。详情请看。在服务器代码和 ES6 模块中使用没问题。 jscs:
disallowIdentifierNames
1
2
3
4
5
6
7
8
9
10
11// bad
const superman = {
default: { clark: 'kent' },
private: true,
};
// good
const superman = {
defaults: { clark: 'kent' }, //default 多了个 s
hidden: true,
};3.3 请使用易懂的符号代替保留字。 jscs:
disallowIdentifierNames
1
2
3
4
5
6
7
8
9
10
11
12
13
14// bad
const superman = {
class: 'alien',
};
// bad
const superman = {
klass: 'alien',
};
// good
const superman = {
type: 'alien',
};3.4 请在创建带有动态属性名的对象时使用可计算的属性名。
因为这样允许一次在一个位置定义对象的所有属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function 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,
};3.5 请使用对象方法的简写。 eslint:
object-shorthand
jscs:requireEnhancedObjectLiterals
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};3.6 请使用属性的简写。 eslint:
object-shorthand
jscs:requireEnhancedObjectLiterals
因为更短且更达意。
1
2
3
4
5
6
7
8
9
10
11const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};3.7 简写属性放在对象声明的头部。
原因是更容易区分哪些属性使用了简写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const 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,
};3.8 只给非法标识符的属性加引号。 eslint:
quote-props
jscs:disallowQuotedKeysInObjects
因为这样更易读,且提供语法高亮,还可以更容易地被很多 JS 引擎优化。
1
2
3
4
5
6
7
8
9
10
11
12
13// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};3.9 请不要直接调用
Object.prototype
的方法。例如hasOwnProperty
、propertyIsEnumerabel
和isPrototypeOf
因为这些方法可能被对象自身的属性覆盖。例如对象
{ hasOwnProperty: false }
又或者是空对象 (Object.create(null)
)1
2
3
4
5
6
7
8
9
10
11
12// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // 在模块作用域中缓存查询。
/* or */
const has = require('has');
…
console.log(has.call(object, key));
数组
4.1 请使用 literal syntax 创建数组。 eslint:
no-array-constructor
1
2
3
4
5// bad
const items = new Array();
// good
const items = [];4.2 请使用 Array#push 添加元素而不是直接复制。
1
2
3
4
5
6
7const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');4.3 请使用数组扩散符
...
拷贝数组。1
2
3
4
5
6
7
8
9
10
11// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];4.4 转化类数组对象到数组时,请使用 Array.from
1
2const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);4.5 请在数组回调方法中使用
return
语句。如果函数体只包含一条语句,可以省略return
。 8.2. eslint:array-callback-return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map(x => x + 1);
// bad
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
flat[index] = flatten;
});
// good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
flat[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;
});
解构
5.1 当访问和使用对象的多个属性时,请使用对象解构。 jscs:
requireObjectDestructuring
因为解构避免创建属性的临时引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 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}`;
}5.2 请使用数组解构。 jscs:
requireArrayDestructuring
1
2
3
4
5
6
7
8const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;5.3 使用对象解构返回多个值,而不是数组解构。 jscs:
disallowArrayDestructuringReturn
因为可以添加新属性或者改变顺序而不用修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 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);
字符串
6.1 请使用单引号
''
括住字符串。 eslint:quotes
jscs:validateQuoteMarks
1
2
3
4
5// bad
const name = "Capt. Janeway";
// good
const name = 'Capt. Janeway';6.2 超过100个字符的字符串应该以多行拼接的方式书写。
6.3 注意:如果滥用,可能会影响性能。 jsPerf 与 讨论.
1
2
3
4
5
6
7
8
9
10
11
12
13// 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.';6.4 动态地拼接字符串时,请使用字符串模板而不是拼接。 eslint:
prefer-template
template-curly-spacing
jscs:requireTemplateStrings
因为字符串模板易读,语法简洁,行数适中,且支持字符串插值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 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}?`;
}6.5 请勿对字符串使用
eval()
,因为会引入太多漏洞。6.6 只在必要时使用转义符。 eslint:
no-useless-escape
因为斜杠损害了可读性,所以若无必要请勿使用。
1
2
3
4
5
6// bad
const foo = '\'this\' \i\s \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
const foo = `'this' is "quoted"`;
函数
7.1 请使用函数声明而不是函数表达式。 jscs:
requireFunctionDeclarations
因为函数声明是有名字的,所以在调用栈中更容易识别出来。而且整个函数声明会被提升,但是只有函数表达式的引用会被提升。这样可以使用 Arrow Functions 代替函数表达式。
1
2
3
4
5
6
7// bad
const foo = function () {
};
// good
function foo() {
}7.2 用括号括住立即执行函数。 eslint:
wrap-iife
jscs:requireParenthesesAroundIIFE
因为用括号括住立即执行函数表达式和调用括号更利落地表达出独立单元的感觉。在模块遍地的世界里,基本上用不到 IIFE 。
1
2
3
4// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());7.3 请勿在非函数区块(if,While 等)中声明函数。请将函数赋值给变量。浏览器虽然允许这么做,但是浏览器间的解释不一样。 eslint:
no-loop-func
7.4 注意: ECMA-262 定义
block
为一系列的语句。函数声明不是语句。 查看 ECMA-262 关于这个问题的记录Read.1
2
3
4
5
6
7
8
9
10
11
12
13
14// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}7.5 请勿将形参命名为
arguments
。这将覆盖每个函数的arguments
对象。1
2
3
4
5
6
7
8
9// bad
function nope(name, options, arguments) {
// ...stuff...
}
// good
function yup(name, options, args) {
// ...stuff...
}7.6 请勿使用
arguments
。请选用不定参数...
。Never usearguments
, opt to use rest syntax...
instead. eslint:prefer-rest-params
因为
...
鲜明地表达传入的参数,再加上,不定参数是货真价实的数组,而不是像arguments
那样的类数组。1
2
3
4
5
6
7
8
9
10// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}7.7 请使用默认参数,而不是改变函数参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// really bad
function handleThings(opts) {
// No! 不应该改变函数参数。
// Double bad: 如果 opts 是可判定为 false 的。它会被设为你可能想要的,但这会引入潜在的 Bug。
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}7.8 避免默认参数的副作用。
因为容易使人困惑,难以理解。
1
2
3
4
5
6
7
8
9var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 37.9 默认参数始终置于最后。
1
2
3
4
5
6
7
8
9// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}7.10 请勿使用函数构造器创建新函数。
因为这样创建函数对字符串求值类似于 eval() ,带来安全隐患。
1
2
3
4
5// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');7.11 请分隔函数签名。
因为保持一致很好,而且不应该在增加或者移除函数名字时增加或移除空格
1
2
3
4
5
6
7
8// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};7.12 请勿修改形参。eslint:
no-param-reassign
因为操纵通过参数传递的对象可能给调用者造成意外的副作用。
1
2
3
4
5
6
7
8
9// bad
function f1(obj) {
obj.key = 1;
};
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};7.13 请勿重新赋值形参。 eslint:
no-param-reassign
原因是重新赋值或导致预料外的行为,特别是在使用
arguments
对象时。也可能造成优化问题,尤其是在 V8 中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 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) {
}
箭头函数
8.1 必须使用函数表达式时(如传递匿名函数时),请使用箭头函数。 eslint:
prefer-arrow-callback
,arrow-spacing
jscs:requireArrowFunctions
为什么呢?因为它创建的函数执行在当前上下文的
this
中,通常是所想要的,而且具有更准确的语义。为什么不呢?当函数相当复杂时,可以将逻辑移入自己的函数中。
1
2
3
4
5
6
7
8
9
10
11// 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;
});8.2 如果函数体中包含单个表达式,请省略括号,使用隐式的 return 。否则保留括号且使用
return
语句。 eslint:arrow-parens
,arrow-body-style
jscs:disallowParenthesesAroundArrowParam
,requireShorthandArrowFunctions
因为这是语法糖。语法糖使得多个函数串联的时候更易读。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 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
}));8.3 请使用括号括住跨行的表达式以便更易读。
原因是这样鲜明地展示了函数开始和结束的位置。
1
2
3
4
5
6
7
8
9
10
11// bad
[1, 2, 3].map(number => 'As time went by, the string containing the ' +
`${number} became much longer. So we needed to break it over multiple ` +
'lines.'
);
// good
[1, 2, 3].map(number => (
`As time went by, the string containing the ${number} became much ` +
'longer. So we needed to break it over multiple lines.'
));8.4 接受单个参数的函数请省略括号。反之,必须用括号括住参数。 eslint:
arrow-parens
jscs:disallowParenthesesAroundArrowParam
Why? Less visual clutter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 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’ve broken it ` +
'over multiple lines!'
));
// 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;
});8.5 请避免混淆箭头函数记号 (
=>
) 和 比较符号 (<=
,>=
) 。 eslint:no-confusing-arrow
1
2
3
4
5
6
7
8// 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) => { return item.height > 256 ? item.largeSize : item.smallSize; };
类 与 构造器
9.1 请使用
class
避免直接操作prototype
。因为
class
语法语义更准确且容易理解。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 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;
}
}9.2 请使用
extends
继承。因为这是不打破
instanceof
,实现继承原型功能的内建方式。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 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];
}
}9.3 方法可以返回
this
以便函数串联。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31// 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);9.4 可以写一个自定义的 toString() 方法,只需保证有用且没有副作用。
1
2
3
4
5
6
7
8
9
10
11
12
13class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}9.5 没有声明构造函数的类拥有默认的构造函数。空的构造函数和仅仅是直接调用父类的构造函数是不必要的。eslint:
no-useless-constructor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 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.6 避免重复的类成员。 eslint:
no-dupe-class-members
因为重复的成员声明默认选择最后一个。拥有重复的成员基本上确定为 bug 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// good
class Foo {
bar() { return 1; }
}
// good
class Foo {
bar() { return 2; }
}
模块
10.1 请使用 modules (
import
/export
) 而不是一个非标准的模块系统。这样可以随时转译到喜欢的模块系统。因为 Modules 是未来。现在开始步入未来吧。
1
2
3
4
5
6
7
8
9
10
11// 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;10.2 请不要使用通配符导入。
这样保证单个默认导出。
1
2
3
4
5// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';10.3 请不要直接在导入语句中导出。
虽然一行更紧凑。但是清晰地导入和清晰地导出更具有一致性。
1
2
3
4
5
6
7
8// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;10.4 只在一个位置导入。eslint:
no-duplicate-imports
从同一个路径多行导入使得代码难以维护。
1
2
3
4
5
6
7
8
9
10
11
12
13// 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';10.5 请不要导出可变的绑定。eslint:
import/no-mutable-exports
因为可变量应当避免通用,尤其是在导出可变的绑定时。可能在某些特殊情况下用到,但是通常情况下应该只导出常量。
1
2
3
4
5
6
7// bad
let foo = 3;
export { foo }
// good
const foo = 3;
export { foo }10.6 模块只有一个导出时,请使用默认导出而不是命名的导出。eslint:
import/prefer-default-export
1
2
3
4
5// bad
export function foo() {}
// good
export default function foo() {}10.7 请将所有的
import
放到非导入语句上面。eslint:import/imports-first
既然
import
会被提升,不如将其置顶以免出现意料之外的行为。1
2
3
4
5
6
7
8
9
10
11// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
迭代器 与 生成器
11.1 请勿使用迭代器。请使用 JavaScript 的高阶函数像
map()
和reduce()
而不是使用循环像for-of
。 eslint:no-iterator
因为这样迫使遵循不可变这个规则。处理只返回值的纯函数比副作用更容易解释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const 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.2 目前请勿使用生成器。
因为转义到ES5效果不好。
11.3 如果非要使用生成器,或者不顾我们的建议,请确保函数签名合理地分隔。 eslint:
generator-star-spacing
因为
function
和*
属于同一个概念的关键字。*
不是function
的修饰器,function*
是一个单独的构造器,与function
不同。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// bad
function * foo() {
}
const bar = function * () {
}
const baz = function *() {
}
const quux = function*() {
}
function*foo() {
}
function *foo() {
}
// very bad
function
*
foo() {
}
const wat = function
*
() {
}
// good
function* foo() {
}
const foo = function* () {
}
属性
12.1 请使用点号访问属性。eslint:
dot-notation
jscs:requireDotNotation
1
2
3
4
5
6
7
8
9
10const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;12.2 以变量访问属性时请使用方括号
[]
。1
2
3
4
5
6
7
8
9
10const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
变量
13.1 请总是使用
const
声明变量。否则将引入全局变量。避免污染全局命名空间。地球超人(Captain Planet)已经警告过我们了。1
2
3
4
5// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();13.2 每个变量请使用一个
const
声明。 eslint:one-var
jscs:disallowMultipleVarDecl
这样更容易增加新的变量声明。而且不用担心是用
,
还是;
,带来标点符号不一致。在调试器中可以步过每一个声明,而不是一次跳过全部。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 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';13.3
const
分为一组,然后let
分为一组。因为这样在重新为变量赋值时很有用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 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;13.4 请在使用变量的地方给变量赋值,但是须放置在合适的位置。
因为
let
和const
都是块级作用域而非函数级作用域。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31// 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;
}
提升
14.1
var
声明会被提升至作用域顶部,相应的赋值却不会。const
和let
声明被一个叫做 临时性死区 (TDZ) 的新概念所保护。所以知道为什么 typeof 不再安全 很重要。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 没用(假设存在 notDefine 全局变量) 。
function example() {
console.log(notDefined); // => 抛出 ReferenceError
}
// 由于变量提升,在引用变量后声明变量是有效的。
// 注意:赋值 `true` 没有被提升。
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// 解释器提升变量声明至作用域顶部。
// 也就是例子可以重写为:
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
// 使用 const 和 let
function example() {
console.log(declaredButNotAssigned); // => 抛出 ReferenceError
console.log(typeof declaredButNotAssigned); // => 抛出 ReferenceError
const declaredButNotAssigned = true;
}14.2 匿名函数的变量名会被提升,而不是函数赋值。
1
2
3
4
5
6
7
8
9function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}14.3 有名字的函数表达式的变量名会被提升,而不是函数名字或者函数体。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function 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');
};
}
// 函数名与变量名一致时也会这样。
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log('named');
}
}14.4 函数声明中的函数名和函数体都会被提升。
1
2
3
4
5
6
7function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}详情请参考 JavaScript 作用域与提升 by Ben Cherry 。
比较符 与 相等性
15.2 条件语句比如
if
语句对其表达式强制使用ToBoolean
求值,且遵循以下几个简单的规则:- Objects 求值为 true
- Undefined 求值为 false
- Null 求值为 false
- Booleans 求值为 相应的布尔值
- Numbers 求值为 false 如果是 +0, -0, 或者 NaN ,否则为 true
- String 求值为 false 如果是空字符串
''
,否则为 true
1
2
3
4if ([0] && []) {
// true
// 数组(甚至是空数组)是对象,对象求值为 true
}15.3 请使用快捷的方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// bad
if (name !== '') {
// ...stuff...
}
// good
if (name) {
// ...stuff...
}
// bad
if (collection.length > 0) {
// ...stuff...
}
// good
if (collection.length) {
// ...stuff...
}15.4 详情见 Angus Croll 的 相等的真相与 JavaScript 。
15.5 请使用括号在包含词法声明(如
let
,const
,function
和class
)的case
和default
中创建区块。因为词法声明在整个
switch
块中是可见的,但是只会在被赋值的时候初始化,这只会发生在相应的case
到达时。引起多个case
定义相同的东西的问题。eslint rules:
no-case-declarations
.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 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 {}
}
}15.6 请勿嵌套三元表达式。一般情况下是单行表达式。
eslint rules:
no-nested-ternary
.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 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;15.7 请避免不需要的三元表达式。
eslint rules:
no-unneeded-ternary
.1
2
3
4
5
6
7
8
9// 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;
块
16.1 多行区块请使用括号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 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;
}16.2 请将
else
放到离if
最近的括号那一行。 eslint:brace-style
jscs:disallowNewlineBeforeBlockStatements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
注释
17.1 多行注释请使用
/** ... */
。包括说明,所有参数的类型和值,以及返回值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...stuff...
return element;
}
// good
/**
* make() returns a new element
* based on the passed in tag name
*
* @param {String} tag
* @return {Element} element
*/
function make(tag) {
// ...stuff...
return element;
}17.2 单行注释请使用
//
。请将单行注释至于前一行。在注释前空一行除非是第一行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33// 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;
}17.3 请在注释中使用
FIXME
或者TODO
前缀以便其他开发者快速理解其中需要被解决的问题,或者问题解决的建议。与其他常规注释不同,这些注释可以被采取行动例如FIXME: -- 需要指出这里
或者TODO: -- 待实现
。17.4 请使用
// FIXME:
声明问题。1
2
3
4
5
6
7
8class Calculator extends Abacus {
constructor() {
super();
// FIXME: 此处不应使用全局变量
total = 0;
}
}17.5 请使用
// TODO:
声明问题的解决方案。1
2
3
4
5
6
7
8class Calculator extends Abacus {
constructor() {
super();
// TODO: total 应该可由参数配置
this.total = 0;
}
}
空白符
18.1 请使用设为两个空格的 soft tabs 。 eslint:
indent
jscs:validateIndentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14// bad
function foo() {
∙∙∙∙const name;
}
// bad
function bar() {
∙const name;
}
// good
function baz() {
∙∙const name;
}18.2 请在第一个大括号前空一格。 eslint:
space-before-blocks
jscs:requireSpaceBeforeBlockStatements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 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',
});18.3 请在控制语句(
if
,while
等)的开始空格前空一格。请勿在函数名字和参数列表间留空。 eslint:keyword-spacing
jscs:requireSpaceAfterKeywords
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}18.4 请使用空格分离操作符。 eslint:
space-infix-ops
jscs:requireSpaceBeforeBinaryOperators
,requireSpaceAfterBinaryOperators
1
2
3
4
5// bad
const x=y+5;
// good
const x = y + 5;18.5 请在文件尾部以换行符结尾。
1
2
3
4// bad
(function (global) {
// ...stuff...
})(this);1
2
3
4
5// bad
(function (global) {
// ...stuff...
})(this);↵
↵1
2
3
4// good
(function (global) {
// ...stuff...
})(this);↵18.6 请在长的方法链(多于两个方法)时使用缩进。请以点开头,这样强调该行为方法调用而不是新的语句。eslint:
newline-per-chained-call
no-whitespace-before-property
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37// 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);18.7 请在区块和下一个语句间空一行。 jscs:
requirePaddingNewLinesAfterBlocks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52// 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;18.8 请勿在块中填充空行。eslint:
padded-blocks
jscs:disallowPaddingNewlinesInBlocks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 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);
}18.9 请勿在括号内加空格。eslint:
space-in-parens
jscs:disallowSpacesInsideParentheses
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}18.10 请勿在放括号中加空格。 eslint:
array-bracket-spacing
jscs:disallowSpacesInsideArrayBrackets
1
2
3
4
5
6
7// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);18.11 请在花括号中加空格。eslint:
object-curly-spacing
jscs:requireSpacesInsideObjectBrackets
1
2
3
4
5// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };18.12 请避免单行超过100个字符(含空白符)。 eslint:
max-len
jscs:maximumLineLength
这样确保了可读性与可维护性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// bad
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';
// 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 = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
'Whatever wizard constrains a helpful ally. The counterpart ascends!';
// good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
逗号
19.1 逗号开头: 别啦 eslint:
comma-style
jscs:requireCommaBeforeLineBreak
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 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',
};19.2 附加的尾部逗号: 好滴 eslint:
comma-dangle
jscs:requireTrailingComma
因为这样 git diffs 会更清晰。而且转译器比如 Babel 会移除尾部附加的逗号。这样不用担心在传统浏览器中的 尾部逗号问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb graph', 'modern nursing']
};
// good - git diff with trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
// bad
const hero = {
firstName: 'Dana',
lastName: 'Scully'
};
const heroes = [
'Batman',
'Superman'
];
// good
const hero = {
firstName: 'Dana',
lastName: 'Scully',
};
const heroes = [
'Batman',
'Superman',
];
分号
20.1 好滴 eslint:
semi
jscs:requireSemicolons
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 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;
}());
类型转换 与 强制转换
21.1 语句还是前执行强制转换。
21.2 Strings:
1
2
3
4
5
6
7
8
9
10// => this.reviewScore = 9;
// bad
const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString(); // 不保证返回 string
// good
const totalScore = String(this.reviewScore);21.3 Numbers: 请在类型转换时使用
Number
,在使用parseInt
解析字符串时带上进制。 eslint:radix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const 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);21.4 如果因为某种原因做了些很野性的事情,而且
parseInt
成为瓶颈,因为性能原因使用位移操作,请留注释解释原因和内容。1
2
3
4
5
6// good
/**
* parseInt 是代码变慢的罪魁祸首。
* 位移字符串强制将它转成 Number 会快很多。
*/
const val = inputValue >> 0;21.5 注意: 请在使用位移操作小心。Number 是 64位值,而位移操作总是返回32位整数 (source)。位移可能导致整数值大于32位。 相关讨论。最大的有符号32位值为2,147,483,647:
1
2
32147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -214748364721.6 Booleans:
1
2
3
4
5
6
7
8
9
10const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
命名约定
22.1 请避免使用单个字母名字。名字应具有自述性。
1
2
3
4
5
6
7
8
9// bad
function q() {
// ...stuff...
}
// good
function query() {
// ..stuff..
}22.2 请在命名对象,函数和实例时使用小驼峰式(camelCase)。eslint:
camelcase
jscs:requireCamelCaseOrUpperCaseIdentifiers
1
2
3
4
5
6
7
8// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}22.3 请仅在命名构造函数和类时使用大驼峰式。eslint:
new-cap
jscs:requireCapitalizedConstructors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 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',
});22.4 请勿使用拖尾或者领头的下划线。eslint:
no-underscore-dangle
jscs:disallowDanglingUnderscores
因为 JavaScript 对于属性和方法而言没有私有性。虽然领头的下划线是一种约定俗成表达 private 的方式。实际上,这些属性全是 public 而且术语公共的 API 约定。这种约定可能会导致开发者错误地以为改变不算做打破或者不需要测试。太长不看:需要
private
的东西时,不能显示地表现(给其他人)。1
2
3
4
5
6
7// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';
// good
this.firstName = 'Panda';22.5 请勿保存
this
的引用。请使用箭头函数或者 bind 函数。 jscs:disallowNodeTypes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 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);
};
}22.6 文件名应当与默认导出的名字完全一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 文件 1 内容
class CheckBox {
// ...
}
export default CheckBox;
// 文件 2 内容
export default function fortyTwo() { return 42; }
// 文件 3 内容
export default function insideDirectory() {}
// 在同一个其他文件
// bad
import CheckBox from './checkBox'; // 大驼峰 import/export ,小驼峰文件名
import FortyTwo from './FortyTwo'; // 大驼峰 import/filename ,小驼峰 export
import InsideDirectory from './InsideDirectory'; // 大驼峰 import/filename ,小驼峰 export
// bad
import CheckBox from './check_box'; // 大驼峰 import/export ,蛇_型文件名。
import forty_two from './forty_two'; // 蛇_型 import/export ,小驼峰 export
import inside_directory from './inside_directory'; // 蛇_型 import ,小驼峰 export
import index from './inside_directory/index'; // 显示地导入 index 文件
import insideDirectory from './insideDirectory/index'; // 显示地导入 index 文件
// good
import CheckBox from './CheckBox'; // 大驼峰 export/import/文件名
import fortyTwo from './fortyTwo'; // 小驼峰 export/import/文件名
import insideDirectory from './insideDirectory'; // 小驼峰 export/import/目录名/ ,隐式的"index"
// ^ 同时支持 insideDirectory.js 和 insideDirectory/index.js22.7 请在导出默认函数时使用小驼峰。文件名应当与函数名一致。
1
2
3
4function makeStyleGuide() {
}
export default makeStyleGuide;22.8 请在导出 构造器 / 类 / 单例 / 函数库 / 裸露的对象 时使用大驼峰。
1
2
3
4
5
6const AirbnbStyleGuide = {
es6: {
}
};
export default AirbnbStyleGuide;
访问器
23.1 属性的访问函数是不必要的。
23.2 请勿使用 JavaScript 的 getters/setters 。因为会造成副作用,难以调试,维护和理清。相应地,如果有访问函数,请使用 getVal() 和 setVal(‘hello’) 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// bad
class Dragon {
get age() {
// ...
}
set age(value) {
// ...
}
}
// good
class Dragon {
getAge() {
// ...
}
setAge(value) {
// ...
}
}23.3 如果属性或方法是
boolean
请使用isVal()
或者hasVal()
。1
2
3
4
5
6
7
8
9// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}23.4 可以创建 get() 和 set() 函数,但请保持一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key];
}
}
事件
24.1 当给事件传递数据时(无论是 DOM 事件还是 Backbone 事件),传递一个带索引的对象而不是原始值。这样随后的贡献者可以在不更新每个处理函数的情况下添加更多数据。例如,相比于:
1
2
3
4
5
6
7
8// bad
$(this).trigger('listingUpdated', listing.id);
...
$(this).on('listingUpdated', (e, listingId) => {
// do something with listingId
});不如:
1
2
3
4
5
6
7
8// good
$(this).trigger('listingUpdated', { listingId: listing.id });
...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingId
});
jQuery
25.1 请给 jQuery对象加上
$
前缀。 jscs:requireDollarBeforejQueryAssignment
1
2
3
4
5
6
7
8// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
// good
const $sidebarBtn = $('.sidebar-btn');25.2 缓存 jQuery 查询。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// bad
function setSidebar() {
$('.sidebar').hide();
// ...stuff...
$('.sidebar').css({
'background-color': 'pink'
});
}
// good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide();
// ...stuff...
$sidebar.css({
'background-color': 'pink'
});
}25.3 DOM查询请使用自上而下
$('.sidebar ul')
或者 父 > 子$('.sidebar > ul')
。 jsPerf25.4 请在 jQuery 对象中查询时使用
find
。1
2
3
4
5
6
7
8
9
10
11
12
13
14// bad
$('ul', '.sidebar').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good
$sidebar.find('ul').hide();
ECMAScript 5 兼容性
- 26.1 参见 Kangax 的 ES5 compatibility table.
ECMAScript 6 风格
- 27.1 以下是 ES6 特性的链接集合。