A mostly reasonable approach to JavaScript
一个最讲道理的JavaScript方法论
Other Style Guides
目录
略
Types 类型
- [1.1] Primitives: When you access a primitive type you work directly on its value.
基本类型: 存取原始值的时候直接操作它的值。
- string
- number
- boolean
- null
- undefined
1
2
3
4
5
6
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
- [1.2] Complex: When you access a complex type you work on a reference to its value.
复杂类型: 存取复杂值的时候直接操作它的引用。
- `object`
- `array`
- `function`
1
2
3
4
5
6
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
References 引用
[2.1] Use
const
for all of your references; avoid usingvar
. eslint:prefer-const
,no-const-assign
所有引用都使用const
,避免使用var
。Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.
为什么?这样可以保证你不重新赋值引用导致 bug,另外更加容易理解。
1
2
3
4
5
6
7// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;[2.2] If you must reassign references, use
let
instead ofvar
. eslint:no-var
jscs:disallowVar
如果一定要重新赋值,用let
而不是var
。Why?
let
is block-scoped rather than function-scoped likevar
.为什么?
let
其作用域为该语句所在的代码块,而不像var
的作用域为该语句所在的函数。 PS:let
没有变量提升。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] Note that both
let
andconst
are block-scoped.
注意let
和const
都是块作用域。1
2
3
4
5
6
7// 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
Objects 对象
[3.1] Use the literal syntax for object creation. eslint:
no-new-object
创建对象的时候使用字面语法。1
2
3
4
5// bad
const item = new Object();
// good
const item = {};[3.2] Use computed property names when creating objects with dynamic property names.
有动态计算属性名需求的时候使用计算属性名。Why? They allow you to define all the properties of an object in one place.
为什么?他们允许你在一个地方定义一个对象所有的属性。
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.3] Use object method shorthand. 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.4] Use property value shorthand. eslint:
object-shorthand
jscs:requireEnhancedObjectLiterals
使用速记法的对象属性。Why? It is shorter to write and descriptive.
为什么?写起来和描述更短。
1
2
3
4
5
6
7
8
9
10
11const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};[3.5] Group your shorthand properties at the beginning of your object declaration.
给你的速记对象分组并放在对象定义的头部。Why? It’s easier to tell which properties are using the shorthand.
为什么?更容易了解哪些变量使用了速记法。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.6] Only quote properties that are invalid identifiers. eslint:
quote-props
jscs:disallowQuotedKeysInObjects
只有属性名不合法的时候使用引号。Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
为什么?一般来说我们主观上认为它更容易阅读,它提升了语法高亮,还更容易被 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.7] Do not call
Object.prototype
methods directly, such ashasOwnProperty
,propertyIsEnumerable
, andisPrototypeOf
.
不要直接调用Object.prototype
的方法,比如hasOwnProperty
,propertyIsEnumerable
和isPrototypeOf
。Why? These methods may be shadowed by properties on the object in question - consider
{ hasOwnProperty: false }
- or, the object may be a null object (Object.create(null)
).
为什么? 这些方法可能被属性遮挡掉,考虑这种情况{ 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; // cache the lookup once, in module scope.
/* or */
import has from 'has';
// ...
console.log(has.call(object, key));[3.8] Prefer the object spread operator over
Object.assign
to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted.
浅拷贝对象最好通过Object.assign
传递对象。使用对象剩余操作符来得到包含省略属性的新对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 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 }
//PS: ES7 feature
Arrays 数组
[4.1] Use the literal syntax for array creation. eslint:
no-array-constructor
数组创建使用字面语法。1
2
3
4
5// bad
const items = new Array();
// good
const items = [];[4.2] Use Array#push instead of direct assignment to add items to an array.
使用 Array.push 而不要直接通过直接赋值来添加元素1
2
3
4
5
6
7const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');[4.3] Use array spreads
...
to copy arrays.
使用数组传递...
来拷贝数组。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 += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];[4.4] To convert an array-like object to an array, use Array.from.
使用 Array.from 来把长得像数组的对象转换成数组。1
2
3
4
5
6const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
//PS: array-like object是有 length 属性且key为非负数的对象
//const x = {1:1,2:3,3:8,length:4}
//const y = Array.from(x)[4.5] Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following [8.2]. eslint:
array-callback-return
在数组回调方法里使用返回语句。如果函数主体只有一个句子,且返回无副作用的表达式,可以省略 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;
});[4.6] Use line breaks after open and before close array brackets if an array has multiple lines.
如果一个数组有多行,在左数组括号之前或者右数组括号之后使用分行。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
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,
];
Destructuring 解构
[5.1] Use object destructuring when accessing and using multiple properties of an object. jscs:
requireObjectDestructuring
当存取或者使用一个对象的多个属性时,使用对象解构。Why? Destructuring saves you from creating temporary references for those properties.
为什么? 解构可以帮助你为这些属性创建临时的引用。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]Use array destructuring. 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] Use object destructuring for multiple return values, not array destructuring. jscs:
disallowArrayDestructuringReturn
为了返回对个值可以使用对象解构,不要用数组解构。Why? You can add new properties over time or change the order of things without breaking call sites.
为什么? 过一段时间你还可以加新的属性,改变东西的顺序的时候不改变调用函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// bad
function processInput(input) {
// then a miracle occurs
return [left, right, top, bottom];
}
// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// then a miracle occurs
return { left, right, top, bottom };
}
// the caller selects only the data they need
const { left, top } = processInput(input);
Strings 字符串
[6.1] Use single quotes
''
for strings. eslint:quotes
jscs:validateQuoteMarks
使用单引号当字符串。1
2
3
4
5
6
7
8
9// bad
const name = "Capt. Janeway";
// bad - template literals should contain interpolation or newlines
//模板语法应该包含插入变量或者本身是多行
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';[6.2] Strings that cause the line to go over 100 characters should not be written across multiple lines using string concatenation.
导致某行超过100个字符的字符串应该使用连接符写成多行。Why? Broken strings are painful to work with and make code less searchable.
为什么? 打断字符串是一个十分蛋疼的事情,导致它不好被搜索。
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.3]When programmatically building up strings, use template strings instead of concatenation. eslint:
prefer-template
template-curly-spacing
jscs:requireTemplateStrings
用编程的方式构建字符串的时候,应该使用模板字符串而不是连接符。Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
为什么?模板字符串给了一个更可读,更简明的语法来包含换行符和插入字符串变量。
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.4] Never use
eval()
on a string, it opens too many vulnerabilities. eslint:no-eval
永远不要在字符串里面使用 eval() ,这玩意缺陷太多。[6.5] Do not unnecessarily escape characters in strings. eslint:
no-useless-escape
字符串里不要使用不必要的转义字符。Why? Backslashes harm readability, thus they should only be present when necessary.
为什么?反斜杠伤害可读性,因此它们应该只在需要的时候出现。
1
2
3
4
5
6// bad
const foo = '\'this\' \i\s \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
Functions 函数
[7.1]Use named function expressions instead of function declarations. eslint:
func-style
jscs:disallowFunctionDeclarations
使用命名函数表达式而不是函数声明。Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to name the expression - anonymous functions can make it harder to locate the problem in an Error’s call stack. (Discussion)
为什么? 函数声明会变量提升,意味着很容易发生在定义之前产生引用。这很伤可读性和可维护性。如果发现一个函数足够大或者足够复杂,甚至会影响你理解剩下的代码,那么
或许此时应该把它提取出来放到它自己的模块里去。别忘了命名表达式,在错误调用栈里匿名函数可能更加难定位问题。1
2
3
4
5
6
7
8
9
10
11
12
13
14// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
const foo = function bar() {
// ...
};[7.2] Wrap immediately invoked function expressions in parentheses. eslint:
wrap-iife
jscs:requireParenthesesAroundIIFE
使用小括号把立即调用的函数表达式包起来。Why? An immediately invoked function expression is a single unit - wrapping both it, and its invocation parens, in parens, cleanly expresses this. Note that in a world with modules everywhere, you almost never need an IIFE.
为什么?一个立即调用的函数表达式是一个单独的单元,把它和它的调用括号括起来可以清楚的表达这个意思。值得注意的是,一个到处是模块的世界里,你几乎不需要立即调用表达式。
1
2
3
4// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());[7.3] Never declare a function in a non-function block (
if
,while
, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint:no-loop-func
绝对不要在一个非函数块里面定义一个函数,比如 if while。应该把函数赋值给一个变量。浏览器会允许你这么做,但是他们用不同的方式解释,这是坏的空头支票。[7.4] Note: ECMA-262 defines a
block
as a list of statements. A function declaration is not a statement. Read ECMA-262’s note on this issue.
注意: ECMA-262 定义了一个 block
作为句子列表。函数声明不是一个句子
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] Never name a parameter
arguments
. This will take precedence over thearguments
object that is given to every function scope.
绝对不要命名成arguments
,这个优先于arguments
对象,被给到每个函数作用域。1
2
3
4
5
6
7
8
9// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}[7.6] Never use
arguments
, opt to use rest syntax...
instead. eslint:prefer-rest-params
绝对不要使用arguments
,可以使用剩余操作符...
替代.Why?
...
is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like likearguments
.为什么?
...
你想要拉的变量是非常明确的,另外,剩余操作符是一个真的数组,不仅仅是一个 Array-list 的对象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] Use default parameter syntax rather than mutating function arguments.
使用默认参数语法而不要使用变化的函数参数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 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 = {}) {
// ...
}[7.8] Avoid side effects with default parameters.
避免默认参数的副作用Why? They are confusing to reason about.
为什么?他们令人困惑到难以推论
1
2
3
4
5
6
7
8
9
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
[7.9] Always put default parameters last.
总是把默认参数放最后。1
2
3
4
5
6
7
8
9// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}[7.10] Never use the Function constructor to create a new function. eslint:
no-new-func
绝对不要使用函数构造函数来创造一个新的函数。Why? Creating a function in this way evaluates a string similarly to eval(), which opens vulnerabilities.
为什么?用这种方法创造一个函数会计算出一个类似于 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] Spacing in a function signature. eslint:
space-before-function-paren
space-before-blocks
函数和括号之间放一个空格。Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name.
为什么?连贯性更好,当你加或者删除名字的时候,没必要去加或者删除一个空格。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] Never mutate parameters. eslint:
no-param-reassign
永远不要改变参数Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.
为什么?操作参数传递进来的对象,可能给原始调用者引起预料之外的副作用。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] Never reassign parameters. eslint:
no-param-reassign
不要给参数重新赋值。Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the
arguments
object. It can also cause optimization issues, especially in V8.
为什么?给参数重新赋值可能导致预期之外的行为,尤其是赋值给arguments
对象。它还会造成性能问题,尤其是 V8 引擎。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 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) {
// ...
}[7.14] Prefer the use of the spread operator
...
to call variadic functions. eslint:prefer-spread
使用传参符号...
来调用可变参函数。Why? It’s cleaner, you don’t need to supply a context, and you can not easily compose
new
withapply
.
为什么? 它更加干净,你不需要提供上下文,你很难去比较new
和apply
。1
2
3
4
5
6
7
8
9
10
11
12
13// 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.15]Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself, with a trailing comma on the last item.
多行声明或者调用的函数应该像任何其他的多行列表一样。每个元素自己占一行,最后一个元素拖一个逗号。
PS: Functions signatures:一个函数由这么几部分组成,函数名、参数个数、参数类型、返回值,函数签名由参数个数与其类型组成。函数在重载时,利用函数签名的不同(即参数个数与类型的不同)来区别调用者到底调用的是那个方法!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 foo(bar,
baz,
quux) {
// ...
}
// good
function foo(
bar,
baz,
quux,
) {
// ...
}
// bad
console.log(foo,
bar,
baz);
// good
console.log(
foo,
bar,
baz,
);
Arrow Functions 箭头函数
[8.1] When you must use function expressions (as when passing an anonymous function), use arrow function notation. eslint:
prefer-arrow-callback
,arrow-spacing
jscs:requireArrowFunctions
当你一定要用一个函数表达式,比如一个匿名函数,使用箭头符号。Why? It creates a version of the function that executes in the context of
this
, which is usually what you want, and is a more concise syntax.Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration.
为什么?它创造了一个在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]If the function body consists of a single statement returning an expression without side effects, omit the braces and use the implicit return. Otherwise, keep the braces and use a
return
statement. eslint:arrow-parens
,arrow-body-style
jscs:disallowParenthesesAroundArrowParam
,requireShorthandArrowFunctions
如果一个函数的主要只有一个句子,返回一个无副作用的表达式,省略括号、返回一个隐式返回值。否则,应该保留括号并且有一个 return 语句。
> Why? Syntactic sugar. It reads well when multiple functions are chained together.
> 为什么?句法糖。多个函数链式调用的时候读起来爽。
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
[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,
}));
// 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;
});
[8.3] In case the expression spans over multiple lines, wrap it in parentheses for better readability.
万一这个表达式有多行,用圆括号把它包起来可以得到更好的可读性。Why? It shows clearly where the function starts and ends.
为什么?它更清楚的告诉你这个函数从哪开始到哪结束。1
2
3
4
5
6
7
8
9
10
11
12
13
14// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));[8.4] If your function takes a single argument and doesn’t use braces, omit the parentheses. Otherwise, always include parentheses around arguments for clarity and consistency. Note: it is also acceptable to always use parentheses, in which case use the “always” option for eslint or do not include
disallowParenthesesAroundArrowParam
for jscs. 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// 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;
});[8.5] Avoid confusing arrow function syntax (
=>
) with comparison operators (<=
,>=
). eslint:no-confusing-arrow
避免使用混淆的箭头符号和比较符号(=>
) (<=
,>=
)。1
2
3
4
5
6
7
8
9
10
11
12
13
14// 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;
};
Classes & Constructors 类&构造器
[9.1]Always use
class
. Avoid manipulatingprototype
directly.
经常使用class
,避免直接操作prototype
Why?
class
syntax is more concise and easier to reason about.
为什么?class语法更简明,更容易推断。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 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] Use
extends
for inheritance.
使用extends
来继承。Why? It is a built-in way to inherit prototype functionality without breaking
instanceof
.
为什么?它是一个内置的方法来继承函数原型,并且不打破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] Methods can return
this
to help with method chaining.
方法可以返回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]It’s okay to write a custom toString() method, just make sure it works successfully and causes no side effects.
写一个定制的 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] Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. 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]Avoid duplicate class members. eslint:
no-dupe-class-members
避免重复类的成员。Why? Duplicate class member declarations will silently prefer the last one - having duplicates is almost certainly a bug.
为什么?重复的对象成员声可能默默的选了后一个,有重复声明基本上肯定是个 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; }
}