JavaScript最合理的方法 A mostly reasonable approach to JavaScript
_
这个指南内容来自Airbnb在github.com上的官方原文和官方中文译本。主体是中文译本,同时把英文原文也合并到一起,以便解决中文译本不够精准的问题。在复制整理的过程中,对于少量中文翻译不准或有错别字的地方,进行了修正。本文后续不会跟踪官方版本的变化,需要了解最新内容的,可以访问下面是各版本的链接。简体中文版的有个别内容的翻译不如繁体中文版本准确,因此也附上链接(当然最好还是直接去看英文版)。

官方英文版:https://github.com/airbnb/javascript
简体中文版:https://github.com/lin-123/javascript
繁体中文版:https://github.com/jigsawye/javascript

Types

1. 基本类型: 你可以直接获取到基本类型的值

1.1 Primitives: When you access a primitive type you work directly on its value.

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol ```javascript const foo = 1; let bar = foo; bar = 9;

console.log(foo, bar); // => 1, 9

  1. Symbols 不能被正确的polyfill 所以在不能原生支持symbol类型的环境[浏览器]中,不应该使用 symbol 类型。
  2. <a name="Bfbp1"></a>
  3. ### 2. 复杂类型: 复杂类型赋值是获取到他的引用的值,相当于传引用
  4. [1.2](https://github.com/airbnb/javascript#types--complex) Complex: When you access a complex type you work on a reference to its value.
  5. - `object`
  6. - `array`
  7. - `function`
  8. ```javascript
  9. const foo = [1, 2];
  10. const bar = foo;
  11. bar[0] = 9;
  12. console.log(foo[0], bar[0]); // => 9, 9

References

3. 所有的赋值都用const,避免使用var

2.1 Use const for all of your references; avoid using var.
eslint: prefer-const, no-const-assign

Why? 因为这个确保你不会改变你的初始值,重复引用会导致bug和代码难以理解

  1. // bad
  2. var a = 1;
  3. var b = 2;
  4. // good
  5. const a = 1;
  6. const b = 2;

4. 如果你一定要对参数重新赋值,那就用let,而不是var

2.2 If you must reassign references, use let instead of var.
eslint: no-var

Why? 因为let是块级作用域,而var是函数级作用域

  1. // bad
  2. var count = 1;
  3. if (true) {
  4. count += 1;
  5. }
  6. // good, use the let.
  7. let count = 1;
  8. if (true) {
  9. count += 1;
  10. }
  • 注意: letconst都是块级作用域

2.3 Note that both let and const are block-scoped.

  1. // const 和 let 都只存在于它定义的那个块级作用域
  2. {
  3. let a = 1;
  4. const b = 1;
  5. }
  6. console.log(a); // ReferenceError
  7. console.log(b); // ReferenceError

Objects

5. 使用字面值创建对象

3.1 Use the literal syntax for object creation.
eslint: no-new-object

  1. // bad
  2. const item = new Object();
  3. // good
  4. const item = {};

6. 当创建一个带有动态属性名的对象时,用计算后的属性名

3.2 Use computed property names when creating objects with dynamic property names.

Why? 这可以使你将定义的所有属性放在对象的一个地方.

  1. function getKey(k) {
  2. return `a key named ${k}`;
  3. }
  4. // bad
  5. const obj = {
  6. id: 5,
  7. name: 'San Francisco',
  8. };
  9. obj[getKey('enabled')] = true;
  10. // good getKey('enabled')是动态属性名
  11. const obj = {
  12. id: 5,
  13. name: 'San Francisco',
  14. [getKey('enabled')]: true,
  15. };

7. 用对象方法简写

3.3 Use object method shorthand.
eslint: object-shorthand

  1. // bad
  2. const atom = {
  3. value: 1,
  4. addValue: function (value) {
  5. return atom.value + value;
  6. },
  7. };
  8. // good
  9. const atom = {
  10. value: 1,
  11. // 对象的方法
  12. addValue(value) {
  13. return atom.value + value;
  14. },
  15. };

8. 用属性值缩写

3.4 Use property value shorthand.
eslint: object-shorthand

Why? 这样写的更简洁且更可读

  1. const lukeSkywalker = 'Luke Skywalker';
  2. // bad
  3. const obj = {
  4. lukeSkywalker: lukeSkywalker,
  5. };
  6. // good
  7. const obj = {
  8. lukeSkywalker,
  9. };

9. 将你的所有缩写放在对象声明的开始

3.5 Group your shorthand properties at the beginning of your object declaration.

Why? 这样也是为了更方便的知道有哪些属性用了缩写.

  1. const anakinSkywalker = 'Anakin Skywalker';
  2. const lukeSkywalker = 'Luke Skywalker';
  3. // bad
  4. const obj = {
  5. episodeOne: 1,
  6. twoJediWalkIntoACantina: 2,
  7. lukeSkywalker,
  8. episodeThree: 3,
  9. mayTheFourth: 4,
  10. anakinSkywalker,
  11. };
  12. // good
  13. const obj = {
  14. lukeSkywalker,
  15. anakinSkywalker,
  16. episodeOne: 1,
  17. twoJediWalkIntoACantina: 2,
  18. episodeThree: 3,
  19. mayTheFourth: 4,
  20. };

10. 只对那些无效的标识使用引号 ''

3.6 Only quote properties that are invalid identifiers.
eslint: quote-props

Why? 通常我们认为这种方式主观上易读。他优化了代码高亮,并且也更容易被许多JS引擎压缩。

  1. // bad
  2. const bad = {
  3. 'foo': 3,
  4. 'bar': 4,
  5. 'data-blah': 5,
  6. };
  7. // good
  8. const good = {
  9. foo: 3,
  10. bar: 4,
  11. 'data-blah': 5,
  12. };

11. 不要直接调用Object.prototype上的方法

hasOwnProperty, propertyIsEnumerable, isPrototypeOf
3.7 Do not call Object.prototype methods directly, such as hasOwnProperty, propertyIsEnumerable, and isPrototypeOf.

Why? 在一些有问题的对象上, 这些方法可能会被屏蔽掉 - 如:{ hasOwnProperty: false } - 或这是一个空对象Object.create(null)

  1. // bad
  2. console.log(object.hasOwnProperty(key));
  3. // good
  4. console.log(Object.prototype.hasOwnProperty.call(object, key));
  5. // best
  6. const has = Object.prototype.hasOwnProperty; // 在模块作用内做一次缓存
  7. /* or */
  8. import has from 'has'; // https://www.npmjs.com/package/has
  9. // ...
  10. console.log(has.call(object, key));

12. 对象浅拷贝时,更推荐使用扩展运算符[就是...运算符],而不是Object.assign

获取对象指定的几个属性时,用对象的rest解构运算符[也是...运算符]更好。
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.

  1. // very bad
  2. const original = { a: 1, b: 2 };
  3. const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
  4. delete copy.a; // so does this
  5. // bad
  6. const original = { a: 1, b: 2 };
  7. const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
  8. // good es6扩展运算符 ...
  9. const original = { a: 1, b: 2 };
  10. // 浅拷贝
  11. const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
  12. // rest 赋值运算符
  13. const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

Arrays

13. 用字面量赋值

4.1 Use the literal syntax for array creation.
eslint: no-array-constructor

  1. // bad
  2. const items = new Array();
  3. // good
  4. const items = [];

14. 用 Array#push代替直接向数组中添加一个值

4.2 Use Array#push instead of direct assignment to add items to an array.

  1. const someStack = [];
  2. // bad
  3. someStack[someStack.length] = 'abracadabra';
  4. // good
  5. someStack.push('abracadabra');

15. 用扩展运算符做数组浅拷贝

4.3 Use array spreads ... to copy arrays.

  1. // bad
  2. const len = items.length;
  3. const itemsCopy = [];
  4. let i;
  5. for (i = 0; i < len; i += 1) {
  6. itemsCopy[i] = items[i];
  7. }
  8. // good
  9. const itemsCopy = [...items];

16. 用 ... 运算符而不是 Array.from来将一个可迭代的对象转换成数组

4.4 To convert an iterable object to an array, use spreads ... instead of Array.from.

  1. const foo = document.querySelectorAll('.foo');
  2. // good
  3. const nodes = Array.from(foo);
  4. // best
  5. const nodes = [...foo];

17. 用 Array.from 去将一个类数组对象转成一个数组

4.5 Use Array.from for converting an array-like object to an array.

  1. const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
  2. // bad
  3. const arr = Array.prototype.slice.call(arrLike);
  4. // good
  5. const arr = Array.from(arrLike);

18. 用 Array.from 而不是 ... 运算符去做map遍历

因为这样可以避免创建一个临时数组。
4.6 Use Array.from instead of spread ... for mapping over iterables, because it avoids creating an intermediate array.

  1. // bad
  2. const baz = [...foo].map(bar);
  3. // good
  4. const baz = Array.from(foo, bar);

19. 在数组方法的回调函数中使用 return 语句

如果函数体由一条返回一个表达式的语句组成, 并且这个表达式没有副作用, 这个时候可以忽略return,详见44 eslint: array-callback-return
4.7 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.

  1. // good
  2. [1, 2, 3].map((x) => {
  3. const y = x + 1;
  4. return x * y;
  5. });
  6. // good 函数只有一个语句
  7. [1, 2, 3].map(x => x + 1);
  8. // bad - 没有返回值, 因为在第一次迭代后acc 就变成undefined了
  9. [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  10. const flatten = acc.concat(item);
  11. acc[index] = flatten;
  12. });
  13. // good
  14. [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  15. const flatten = acc.concat(item);
  16. acc[index] = flatten;
  17. return flatten;
  18. });
  19. // bad
  20. inbox.filter((msg) => {
  21. const { subject, author } = msg;
  22. if (subject === 'Mockingbird') {
  23. return author === 'Harper Lee';
  24. } else {
  25. return false;
  26. }
  27. });
  28. // good
  29. inbox.filter((msg) => {
  30. const { subject, author } = msg;
  31. if (subject === 'Mockingbird') {
  32. return author === 'Harper Lee';
  33. }
  34. return false;
  35. });

20. 如果一个数组有很多行,在数组的 [ 后和 ] 前断行

4.8 Use line breaks after open and before close array brackets if an array has multiple lines

  1. // bad
  2. const arr = [
  3. [0, 1], [2, 3], [4, 5],
  4. ];
  5. const objectInArray = [{
  6. id: 1,
  7. }, {
  8. id: 2,
  9. }];
  10. const numberInArray = [
  11. 1, 2,
  12. ];
  13. // good
  14. const arr = [[0, 1], [2, 3], [4, 5]];
  15. const objectInArray = [
  16. {
  17. id: 1,
  18. },
  19. {
  20. id: 2,
  21. },
  22. ];
  23. const numberInArray = [
  24. 1,
  25. 2,
  26. ];

Destructuring

21. 用对象的解构赋值来获取和使用对象某个或多个属性值

5.1 Use object destructuring when accessing and using multiple properties of an object.
eslint: prefer-destructuring

Why? 解构使您不必为这些属性创建临时引用

  1. // bad
  2. function getFullName(user) {
  3. const firstName = user.firstName;
  4. const lastName = user.lastName;
  5. return `${firstName} ${lastName}`;
  6. }
  7. // good
  8. function getFullName(user) {
  9. const { firstName, lastName } = user;
  10. return `${firstName} ${lastName}`;
  11. }
  12. // best
  13. function getFullName({ firstName, lastName }) {
  14. return `${firstName} ${lastName}`;
  15. }

22. 用数组解构

5.2 Use array destructuring.

  1. const arr = [1, 2, 3, 4];
  2. // bad
  3. const first = arr[0];
  4. const second = arr[1];
  5. // good
  6. const [first, second] = arr;

23. 多个返回值用对象的解构,而不是数组解构

5.3 Use object destructuring for multiple return values, not array destructuring.

Why? 你可以在后期添加新的属性或者变换变量的顺序而不会打破原有的调用

  1. // bad
  2. function processInput(input) {
  3. // 然后就是见证奇迹的时刻
  4. return [left, right, top, bottom];
  5. }
  6. // 调用者需要想一想返回值的顺序
  7. const [left, __, top] = processInput(input);
  8. // good
  9. function processInput(input) {
  10. // oops, 奇迹又发生了
  11. return { left, right, top, bottom };
  12. }
  13. // 调用者只需要选择他想用的值就好了
  14. const { left, top } = processInput(input);

Strings

24. 对string用单引号 ''

6.1 Use single quotes '' for strings.
eslint: quotes

  1. // bad
  2. const name = "Capt. Janeway";
  3. // bad - 样例应该包含插入文字或换行
  4. const name = `Capt. Janeway`;
  5. // good
  6. const name = 'Capt. Janeway';

25. 超过100个字符的字符串不应该用string串联成多行

6.2 Strings that cause the line to go over 100 characters should not be written across multiple lines using string concatenation.

Why? 被折断的字符串工作起来是糟糕的而且使得代码更不易被搜索。

  1. // bad
  2. const errorMessage = 'This is a super long error that was thrown because \
  3. of Batman. When you stop to think about how Batman had anything to do \
  4. with this, you would get nowhere \
  5. fast.';
  6. // bad
  7. const errorMessage = 'This is a super long error that was thrown because ' +
  8. 'of Batman. When you stop to think about how Batman had anything to do ' +
  9. 'with this, you would get nowhere fast.';
  10. // good
  11. 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.';

26. 用字符串模板而不是字符串拼接来组织可编程字符串

6.3 When programmatically building up strings, use template strings instead of concatenation.
eslint: prefer-template template-curly-spacing

Why? 模板字符串更具可读性、语法简洁、字符串插入参数。

  1. // bad
  2. function sayHi(name) {
  3. return 'How are you, ' + name + '?';
  4. }
  5. // bad
  6. function sayHi(name) {
  7. return ['How are you, ', name, '?'].join();
  8. }
  9. // bad
  10. function sayHi(name) {
  11. return `How are you, ${ name }?`;
  12. }
  13. // good
  14. function sayHi(name) {
  15. return `How are you, ${name}?`;
  16. }

27. 永远不要在字符串中用eval(),他就是潘多拉盒子

6.4 Never use eval() on a string, it opens too many vulnerabilities.
eslint: no-eval

28. 不要使用不必要的转义字符

6.5 Do not unnecessarily escape characters in strings.
eslint: no-useless-escape

Why? 反斜线可读性差,所以他们只在必须使用时才出现

  1. // bad
  2. const foo = '\'this\' \i\s \"quoted\"';
  3. // good
  4. const foo = '\'this\' is "quoted"';
  5. //best
  6. const foo = `my name is '${name}'`;

Functions

29. 用命名函数表达式而不是函数声明

7.1 Use named function expressions instead of function declarations.
eslint: func-style

函数表达式: const func = function () {} 函数声明: function func() {} Why? 函数声明时作用域被提前了,这意味着在一个文件里函数很容易(太容易了)在其定义之前被引用。这样伤害了代码可读性和可维护性。如果你发现一个函数又大又复杂,这个函数妨碍这个文件其他部分的理解性,这可能就是时候把这个函数单独抽成一个模块了。别忘了给表达式显示的命名,不用管这个名字是不是由一个确定的变量推断出来的,这消除了由匿名函数在错误调用栈产生的所有假设,这在现代浏览器和类似babel编译器中很常见 (Discussion) 这一段还不理解这种错误发生的场景,所以只能直译过来了, 另附原文 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 explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions made about the Error’s call stack. (Discussion)

  1. // bad
  2. function foo() {
  3. // ...
  4. }
  5. // bad
  6. const foo = function () {
  7. // ...
  8. };
  9. // good
  10. // lexical name distinguished from the variable-referenced invocation(s)
  11. // 函数表达式名和声明的函数名是不一样的
  12. const short = function longUniqueMoreDescriptiveLexicalFoo() {
  13. // ...
  14. };

30. 把立即执行函数包裹在圆括号里

7.2 Wrap immediately invoked function expressions in parentheses.
eslint: wrap-iife

Why? immediately invoked function expression = IIFE Why? 一个立即调用的函数表达式是一个单元 - 把它和他的调用者(圆括号)包裹起来,在括号中可以清晰的地表达这些。 Why? 注意:在模块化世界里,你几乎用不着 IIFE

  1. // immediately-invoked function expression (IIFE)
  2. (function () {
  3. console.log('Welcome to the Internet. Please follow me.');
  4. }());

31. 不要在非函数块(if、while等等)内声明函数

把这个函数分配给一个变量。浏览器会允许你这样做,但浏览器解析方式不同,这是一个坏消息(详见no-loop-func)。
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
Note: 在ECMA-262中 [块 block] 的定义是: 一系列的语句; 但是函数声明不是一个语句。 函数表达式是一个语句。
7.4 Note: ECMA-262 defines a block as a list of statements. A function declaration is not a statement.

  1. // bad
  2. if (currentUser) {
  3. function test() {
  4. console.log('Nope.');
  5. }
  6. }
  7. // good
  8. let test;
  9. if (currentUser) {
  10. test = () => {
  11. console.log('Yup.');
  12. };
  13. }

32. 不要用arguments命名参数

他的优先级高于每个函数作用域自带的 arguments 对象, 这会导致函数自带的 arguments 值被覆盖
7.5 Never name a parameter arguments. This will take precedence over the arguments object that is given to every function scope.

  1. // bad
  2. function foo(name, options, arguments) {
  3. // ...
  4. }
  5. // good
  6. function foo(name, options, args) {
  7. // ...
  8. }

33. 不要使用arguments,用rest语法...代替

7.6 Never use arguments, opt to use rest syntax ... instead.
eslint: prefer-rest-params

Why? ...明确你想用那个参数。而且rest参数是真数组,而不是类似数组的arguments

  1. // bad
  2. function concatenateAll() {
  3. const args = Array.prototype.slice.call(arguments);
  4. return args.join('');
  5. }
  6. // good
  7. function concatenateAll(...args) {
  8. return args.join('');
  9. }

34. 用默认参数语法而不是在函数里对参数重新赋值

7.7 Use default parameter syntax rather than mutating function arguments.

  1. // really bad
  2. function handleThings(opts) {
  3. // 不, 我们不该改arguments
  4. // 第二: 如果 opts 的值为 false, 它会被赋值为 {}
  5. // 虽然你想这么写, 但是这个会带来一些细微的bug
  6. opts = opts || {};
  7. // ...
  8. }
  9. // still bad
  10. function handleThings(opts) {
  11. if (opts === void 0) {
  12. opts = {};
  13. }
  14. // ...
  15. }
  16. // good
  17. function handleThings(opts = {}) {
  18. // ...
  19. }

35. 默认参数避免副作用

7.8 Avoid side effects with default parameters.

Why? 他会令人迷惑不解, 比如下面这个, a到底等于几, 这个需要想一下。

  1. var b = 1;
  2. // bad
  3. function count(a = b++) {
  4. console.log(a);
  5. }
  6. count(); // 1
  7. count(); // 2
  8. count(3); // 3
  9. count(); // 3

36. 把默认参数赋值放在最后

7.9 Always put default parameters last.

  1. // bad
  2. function handleThings(opts = {}, name) {
  3. // ...
  4. }
  5. // good
  6. function handleThings(name, opts = {}) {
  7. // ...
  8. }

37. 不要用函数构造器创建函数

7.10 Never use the Function constructor to create a new function.
eslint: no-new-func

Why? 以这种方式创建函数将类似于字符串 eval(),这会打开漏洞。

  1. // bad
  2. var add = new Function('a', 'b', 'return a + b');
  3. // still bad
  4. var subtract = Function('a', 'b', 'return a - b');

38. 函数签名部分要有空格

7.11 Spacing in a function signature.
eslint: space-before-function-paren space-before-blocks

Why? 统一性好,而且在你添加/删除一个名字的时候不需要添加/删除空格

  1. // bad
  2. const f = function(){};
  3. const g = function (){};
  4. const h = function() {};
  5. // good
  6. const x = function () {};
  7. const y = function a() {};

39. 不要改参数

7.12 Never mutate parameters.
eslint: no-param-reassign

Why? 操作参数对象对原始调用者会导致意想不到的副作用。 就是不要改参数的数据结构,保留参数原始值和数据结构。

  1. // bad
  2. function f1(obj) {
  3. obj.key = 1;
  4. };
  5. // good
  6. function f2(obj) {
  7. const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
  8. };

40. 不要对参数重新赋值

7.13 Never reassign parameters.
eslint: no-param-reassign

Why? 参数重新赋值会导致意外行为,尤其是对 arguments。这也会导致优化问题,特别是在V8里

  1. // bad
  2. function f1(a) {
  3. a = 1;
  4. // ...
  5. }
  6. function f2(a) {
  7. if (!a) { a = 1; }
  8. // ...
  9. }
  10. // good
  11. function f3(a) {
  12. const b = a || 1;
  13. // ...
  14. }
  15. function f4(a = 1) {
  16. // ...
  17. }

41. 用spread操作符...去调用多变的函数更好

7.14 Prefer the use of the spread operator ... to call variadic functions.
eslint: prefer-spread

Why? 这样更清晰,你不必提供上下文,而且你不能轻易地用apply来组成new

  1. // bad
  2. const x = [1, 2, 3, 4, 5];
  3. console.log.apply(console, x);
  4. // good
  5. const x = [1, 2, 3, 4, 5];
  6. console.log(...x);
  7. // bad
  8. new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
  9. // good
  10. new Date(...[2016, 8, 5]);

42. 调用或者书写包含多个参数的函数应该每行只包含一个参数,逗号结尾

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.

  1. // bad
  2. function foo(bar,
  3. baz,
  4. quux) {
  5. // ...
  6. }
  7. // good 缩进不要太过分
  8. function foo(
  9. bar,
  10. baz,
  11. quux,
  12. ) {
  13. // ...
  14. }
  15. // bad
  16. console.log(foo,
  17. bar,
  18. baz);
  19. // good
  20. console.log(
  21. foo,
  22. bar,
  23. baz,
  24. );

Arrow Functions

43. 如果一定要用匿名函数(在回调函数里)的时候用箭头表达式

8.1 When you must use an anonymous function (as when passing an inline callback), use arrow function notation.
eslint: prefer-arrow-callback, arrow-spacing

Why? 他创建了一个this的当前执行上下文的函数的版本,这通常就是你想要的;而且箭头函数是更简洁的语法 Why? 什么时候不用箭头函数: 如果你有一个相当复杂的函数,你可能会把这个逻辑移出到他自己的函数声明里。

  1. // bad
  2. [1, 2, 3].map(function (x) {
  3. const y = x + 1;
  4. return x * y;
  5. });
  6. // good
  7. [1, 2, 3].map((x) => {
  8. const y = x + 1;
  9. return x * y;
  10. });

44. 如果函数体由一个没有副作用的表达式语句组成,删除大括号和return。否则,继续用大括号和 return 语句

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

Why? 语法糖,当多个函数链在一起的时候好读

  1. // bad
  2. [1, 2, 3].map(number => {
  3. const nextNumber = number + 1;
  4. `A string containing the ${nextNumber}.`;
  5. });
  6. // good
  7. [1, 2, 3].map(number => `A string containing the ${number}.`);
  8. // good
  9. [1, 2, 3].map((number) => {
  10. const nextNumber = number + 1;
  11. return `A string containing the ${nextNumber}.`;
  12. });
  13. // good
  14. [1, 2, 3].map((number, index) => ({
  15. [index]: number
  16. }));
  17. // 表达式有副作用就不要用隐式return
  18. function foo(callback) {
  19. const val = callback();
  20. if (val === true) {
  21. // Do something if callback returns true
  22. }
  23. }
  24. let bool = false;
  25. // bad
  26. // 这种情况会return bool = true, 不好
  27. foo(() => bool = true);
  28. // good
  29. foo(() => {
  30. bool = true;
  31. });

45. 如果表达式涉及多行,把他包裹在圆括号里

8.3 In case the expression spans over multiple lines, wrap it in parentheses for better readability.

Why? 这样清晰的显示函数的开始和结束

  1. // bad
  2. ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
  3. httpMagicObjectWithAVeryLongName,
  4. httpMethod
  5. )
  6. );
  7. // good
  8. ['get', 'post', 'put'].map(httpMethod => (
  9. Object.prototype.hasOwnProperty.call(
  10. httpMagicObjectWithAVeryLongName,
  11. httpMethod
  12. )
  13. ));

46. 如果你的函数只有一个参数并且函数体没有大括号,就删除圆括号。否则,参数总是放在圆括号里

8.4 Always include parentheses around arguments for clarity and consistency.
注意: 一直用圆括号也是没问题,只需要配置 “always” option for eslint
eslint: arrow-parens

Why? 这样少一些混乱, 其实没啥语法上的讲究,就保持一个风格。

  1. // bad
  2. [1, 2, 3].map((x) => x * x);
  3. // good
  4. [1, 2, 3].map(x => x * x);
  5. // good
  6. [1, 2, 3].map(number => (
  7. `A long string with the ${number}. Its so long that we dont want it to take up space on the .map line!`
  8. ));
  9. // bad
  10. [1, 2, 3].map(x => {
  11. const y = x + 1;
  12. return x * y;
  13. });
  14. // good
  15. [1, 2, 3].map((x) => {
  16. const y = x + 1;
  17. return x * y;
  18. });

47. 避免箭头函数(=>)和比较操作符(<=, >=)混淆

8.5 Avoid confusing arrow function syntax (=>) with comparison operators (<=, >=).
eslint: no-confusing-arrow

  1. // bad
  2. const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize;
  3. // bad
  4. const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
  5. // good
  6. const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
  7. // good
  8. const itemHeight = (item) => {
  9. const { height, largeSize, smallSize } = item;
  10. return height <= 256 ? largeSize : smallSize;
  11. };

48. 在隐式return中强制约束函数体的位置, 就写在箭头后面

8.6 Enforce the location of arrow function bodies with implicit returns.
eslint: implicit-arrow-linebreak

  1. // bad
  2. (foo) =>
  3. bar;
  4. (foo) =>
  5. (bar);
  6. // good
  7. (foo) => bar;
  8. (foo) => (bar);
  9. (foo) => (
  10. bar
  11. )

Classes & Constructors

49. 常用class,避免直接操作prototype

9.1 Always use class. Avoid manipulating prototype directly.

Why? class语法更简洁更易理解

  1. // bad
  2. function Queue(contents = []) {
  3. this.queue = [...contents];
  4. }
  5. Queue.prototype.pop = function () {
  6. const value = this.queue[0];
  7. this.queue.splice(0, 1);
  8. return value;
  9. };
  10. // good
  11. class Queue {
  12. constructor(contents = []) {
  13. this.queue = [...contents];
  14. }
  15. pop() {
  16. const value = this.queue[0];
  17. this.queue.splice(0, 1);
  18. return value;
  19. }
  20. }

50. 用extends实现继承

9.2 Use extends for inheritance.

Why? 它是一种内置的方法来继承原型功能而不打破instanceof

  1. // bad
  2. const inherits = require('inherits');
  3. function PeekableQueue(contents) {
  4. Queue.apply(this, contents);
  5. }
  6. inherits(PeekableQueue, Queue);
  7. PeekableQueue.prototype.peek = function () {
  8. return this.queue[0];
  9. }
  10. // good
  11. class PeekableQueue extends Queue {
  12. peek() {
  13. return this.queue[0];
  14. }
  15. }

51. 可以返回this来实现方法链

9.3 Methods can return this to help with method chaining.

  1. // bad
  2. Jedi.prototype.jump = function () {
  3. this.jumping = true;
  4. return true;
  5. };
  6. Jedi.prototype.setHeight = function (height) {
  7. this.height = height;
  8. };
  9. const luke = new Jedi();
  10. luke.jump(); // => true
  11. luke.setHeight(20); // => undefined
  12. // good
  13. class Jedi {
  14. jump() {
  15. this.jumping = true;
  16. return this;
  17. }
  18. setHeight(height) {
  19. this.height = height;
  20. return this;
  21. }
  22. }
  23. const luke = new Jedi();
  24. luke.jump()
  25. .setHeight(20);

52. 写一个定制的toString() 方法是可以的,只要保证它是可以正常工作且没有副作用的

9.4 It’s okay to write a custom toString() method, just make sure it works successfully and causes no side effects.

  1. class Jedi {
  2. constructor(options = {}) {
  3. this.name = options.name || 'no name';
  4. }
  5. getName() {
  6. return this.name;
  7. }
  8. toString() {
  9. return `Jedi - ${this.getName()}`;
  10. }
  11. }

53. 如果没有具体说明,类有默认的构造方法。一个空的构造函数或只是代表父类的构造函数是不需要写的

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. // bad
  2. class Jedi {
  3. constructor() {}
  4. getName() {
  5. return this.name;
  6. }
  7. }
  8. // bad
  9. class Rey extends Jedi {
  10. // 这种构造函数是不需要写的
  11. constructor(...args) {
  12. super(...args);
  13. }
  14. }
  15. // good
  16. class Rey extends Jedi {
  17. constructor(...args) {
  18. super(...args);
  19. this.name = 'Rey';
  20. }
  21. }

54. 避免重复类成员

9.6 Avoid duplicate class members.
eslint: no-dupe-class-members

Why? 重复类成员会默默的执行最后一个 —— 重复本身也是一个bug

  1. // bad
  2. class Foo {
  3. bar() { return 1; }
  4. bar() { return 2; }
  5. }
  6. // good
  7. class Foo {
  8. bar() { return 1; }
  9. }
  10. // good
  11. class Foo {
  12. bar() { return 2; }
  13. }

55. 除非外部库或框架需要使用特定的非静态方法,否则类方法应该使用this或被做成静态方法。 作为一个实例方法应该表明它根据接收者的属性有不同的行为

9.7 Class methods should use this or be made into a static method unless an external library or framework requires to use specific non-static methods. Being an instance method should indicate that it behaves differently based on properties of the receiver.
eslint: class-methods-use-this

  1. // bad
  2. class Foo {
  3. bar() {
  4. console.log('bar');
  5. }
  6. }
  7. // good - this 被使用了
  8. class Foo {
  9. bar() {
  10. console.log(this.bar);
  11. }
  12. }
  13. // good - constructor 不一定要使用this
  14. class Foo {
  15. constructor() {
  16. // ...
  17. }
  18. }
  19. // good - 静态方法不需要使用 this
  20. class Foo {
  21. static bar() {
  22. console.log('bar');
  23. }
  24. }

Modules

56. 用(import/export) 模块而不是无标准的模块系统

10.1 Always use modules (import/export) over a non-standard module system. You can always transpile to your preferred module system.

Why? 模块化是未来,让我们现在就开启未来吧。

  1. // bad
  2. const AirbnbStyleGuide = require('./AirbnbStyleGuide');
  3. module.exports = AirbnbStyleGuide.es6;
  4. // ok
  5. import AirbnbStyleGuide from './AirbnbStyleGuide';
  6. export default AirbnbStyleGuide.es6;
  7. // best
  8. import { es6 } from './AirbnbStyleGuide';
  9. export default es6;

57. 不要用import通配符, 就是 * 这种方式

10.2 Do not use wildcard imports.

Why? 这确保你有单个默认的导出

  1. // bad
  2. import * as AirbnbStyleGuide from './AirbnbStyleGuide';
  3. // good
  4. import AirbnbStyleGuide from './AirbnbStyleGuide';

58. 不要直接从import中直接export

10.3 And do not export directly from an import.

Why? 虽然一行是简洁的,有一个明确的方式进口和一个明确的出口方式来保证一致性。

  1. // bad
  2. // filename es6.js
  3. export { es6 as default } from './AirbnbStyleGuide';
  4. // good
  5. // filename es6.js
  6. import { es6 } from './AirbnbStyleGuide';
  7. export default es6;

59. 一个路径只 import 一次

10.4 Only import from a path in one place.
eslint: no-duplicate-imports

Why? 从同一个路径下import多行会使代码难以维护

  1. // bad
  2. import foo from 'foo';
  3. // … some other imports … //
  4. import { named1, named2 } from 'foo';
  5. // good
  6. import foo, { named1, named2 } from 'foo';
  7. // good
  8. import foo, {
  9. named1,
  10. named2,
  11. } from 'foo';

60. 不要导出可变的东西

10.5 Do not export mutable bindings.
eslint: import/no-mutable-exports

Why? 变化通常都是需要避免,特别是当你要输出可变的绑定。虽然在某些场景下可能需要这种技术,但总的来说应该导出常量。

  1. // bad
  2. let foo = 3;
  3. export { foo }
  4. // good
  5. const foo = 3;
  6. export { foo }

61. 在一个单一导出模块里,用 export default 更好

10.6 In modules with a single export, prefer default export over named export.
eslint: import/prefer-default-export

Why? 鼓励使用更多文件,每个文件只做一件事情并导出,这样可读性和可维护性更好。

  1. // bad
  2. export function foo() {}
  3. // good
  4. export default function foo() {}

62. import 放在其他所有语句之前

10.7 Put all imports above non-import statements.
eslint: import/first

Why? 让import放在最前面防止意外行为。

  1. // bad
  2. import foo from 'foo';
  3. foo.init();
  4. import bar from 'bar';
  5. // good
  6. import foo from 'foo';
  7. import bar from 'bar';
  8. foo.init();

63. 多行import应该缩进,就像多行数组和对象字面量

10.8 Multiline imports should be indented just like multiline array and object literals.

Why? 花括号与样式指南中每个其他花括号块遵循相同的缩进规则,逗号也是。

  1. // bad
  2. import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
  3. // good
  4. import {
  5. longNameA,
  6. longNameB,
  7. longNameC,
  8. longNameD,
  9. longNameE,
  10. } from 'path';

64. 在import语句里不允许Webpack loader语法

10.9 Disallow Webpack loader syntax in module import statements.

eslint: import/no-webpack-loader-syntax

Why? 一旦用Webpack语法在import里会把代码耦合到模块绑定器。最好是在webpack.config.js里写webpack loader语法

  1. // bad
  2. import fooSass from 'css!sass!foo.scss';
  3. import barCss from 'style!css!bar.css';
  4. // good
  5. import fooSass from 'foo.scss';
  6. import barCss from 'bar.css';

65. 不要使用JavaScript文件扩展名

10.10 Do not include JavaScript filename extensions.
eslint: import/extensions

Why? 包括扩展会抑制重构,并且不适当地将要导入的模块的实现细节硬编码到每个使用者中。

  1. // bad
  2. import foo from './foo.js';
  3. import bar from './bar.jsx';
  4. import baz from './baz/index.jsx';
  5. // good
  6. import foo from './foo';
  7. import bar from './bar';
  8. import baz from './baz';

Iterators and Generators

66. 不要用遍历器。用JavaScript高级函数代替for-infor-of

11.1 Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like for-in or for-of.
eslint: no-iterator no-restricted-syntax

Why? 这强调了我们不可变的规则。 处理返回值的纯函数比副作用更容易。 Why? 用数组的这些迭代方法: map() / every() / filter() / find() / findIndex() / reduce() / some() / … , 用对象的这些方法 Object.keys() / Object.values() / Object.entries() 去产生一个数组, 这样你就能去遍历对象了。

  1. const numbers = [1, 2, 3, 4, 5];
  2. // bad
  3. let sum = 0;
  4. for (let num of numbers) {
  5. sum += num;
  6. }
  7. sum === 15;
  8. // good
  9. let sum = 0;
  10. numbers.forEach(num => sum += num);
  11. sum === 15;
  12. // best (use the functional force)
  13. const sum = numbers.reduce((total, num) => total + num, 0);
  14. sum === 15;
  15. // bad
  16. const increasedByOne = [];
  17. for (let i = 0; i < numbers.length; i++) {
  18. increasedByOne.push(numbers[i] + 1);
  19. }
  20. // good
  21. const increasedByOne = [];
  22. numbers.forEach(num => increasedByOne.push(num + 1));
  23. // best (keeping it functional)
  24. const increasedByOne = numbers.map(num => num + 1);

67. 现在不要用generator

11.2 Don’t use generators for now.

Why? 它在es5上支持的不好

68. 如果一定要用generator,或者你忽略我们的建议,请确保它们的函数签名空格是得当的

11.3 If you must use generators, or if you disregard our advice, make sure their function signature is spaced properly.
eslint: generator-star-spacing

Why? function* 是同一概念关键字 - *不是function的修饰符,function*是一个和function不一样的独特结构

  1. // bad
  2. function * foo() {
  3. // ...
  4. }
  5. // bad
  6. const bar = function * () {
  7. // ...
  8. }
  9. // bad
  10. const baz = function *() {
  11. // ...
  12. }
  13. // bad
  14. const quux = function*() {
  15. // ...
  16. }
  17. // bad
  18. function*foo() {
  19. // ...
  20. }
  21. // bad
  22. function *foo() {
  23. // ...
  24. }
  25. // very bad
  26. function
  27. *
  28. foo() {
  29. // ...
  30. }
  31. // very bad
  32. const wat = function
  33. *
  34. () {
  35. // ...
  36. }
  37. // good
  38. function* foo() {
  39. // ...
  40. }
  41. // good
  42. const foo = function* () {
  43. // ...
  44. }

Properties

69. 访问属性时使用点符号

12.1 Use dot notation when accessing properties.
eslint: dot-notation

  1. const luke = {
  2. jedi: true,
  3. age: 28,
  4. };
  5. // bad
  6. const isJedi = luke['jedi'];
  7. // good
  8. const isJedi = luke.jedi;

70. 当获取的属性是变量时用方括号[]

12.2 Use bracket notation [] when accessing properties with a variable.

  1. const luke = {
  2. jedi: true,
  3. age: 28,
  4. };
  5. function getProp(prop) {
  6. return luke[prop];
  7. }
  8. const isJedi = getProp('jedi');

71. 做幂运算时用幂操作符 **

12.3 Use exponentiation operator ** when calculating exponentiations.
eslint: no-restricted-properties

  1. // bad
  2. const binary = Math.pow(2, 10);
  3. // good
  4. const binary = 2 ** 10;

Variables

72. 用constlet声明变量

不这样做会导致全局变量。 我们要避免污染全局命名空间。
13.1 Always use const or let to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that.
eslint: no-undef prefer-const

  1. // bad
  2. superPower = new SuperPower();
  3. // good
  4. const superPower = new SuperPower();

73. 每个变量都用一个 constlet

13.2 Use one const or let declaration per variable or assignment.
eslint: one-var

Why? 这种方式很容易去声明新的变量,你不用去考虑把;调换成,,或者引入一个只有标点的不同的变化。这种做法也可以是你在调试的时候单步每个声明语句,而不是一下跳过所有声明。

  1. // bad
  2. const items = getItems(),
  3. goSportsTeam = true,
  4. dragonball = 'z';
  5. // bad
  6. // (compare to above, and try to spot the mistake)
  7. const items = getItems(),
  8. goSportsTeam = true;
  9. dragonball = 'z';
  10. // good
  11. const items = getItems();
  12. const goSportsTeam = true;
  13. const dragonball = 'z';

74 const放一起,let放一起

13.3 Group all your consts and then group all your lets.

Why? 在你需要分配一个新的变量, 而这个变量依赖之前分配过的变量的时候,这种做法是有帮助的

  1. // bad
  2. let i, len, dragonball,
  3. items = getItems(),
  4. goSportsTeam = true;
  5. // bad
  6. let i;
  7. const items = getItems();
  8. let dragonball;
  9. const goSportsTeam = true;
  10. let len;
  11. // good
  12. const goSportsTeam = true;
  13. const items = getItems();
  14. let dragonball;
  15. let i;
  16. let length;

75. 在你需要的地方声明变量,但是要放在合理的位置

13.4 Assign variables where you need them, but place them in a reasonable place.

Why? letconst 都是块级作用域而不是函数级作用域

  1. // bad - unnecessary function call
  2. function checkName(hasName) {
  3. const name = getName();
  4. if (hasName === 'test') {
  5. return false;
  6. }
  7. if (name === 'test') {
  8. this.setName('');
  9. return false;
  10. }
  11. return name;
  12. }
  13. // good
  14. function checkName(hasName) {
  15. if (hasName === 'test') {
  16. return false;
  17. }
  18. // 在需要的时候分配
  19. const name = getName();
  20. if (name === 'test') {
  21. this.setName('');
  22. return false;
  23. }
  24. return name;
  25. }

76. 不要使用链接变量分配

13.5 Don’t chain variable assignments.
eslint: no-multi-assign

Why? 链接变量分配创建隐式全局变量。

  1. // bad
  2. (function example() {
  3. // JavaScript 将这一段解释为
  4. // let a = ( b = ( c = 1 ) );
  5. // let 只对变量 a 起作用; 变量 b 和 c 都变成了全局变量
  6. let a = b = c = 1;
  7. }());
  8. console.log(a); // undefined
  9. console.log(b); // 1
  10. console.log(c); // 1
  11. // good
  12. (function example() {
  13. let a = 1;
  14. let b = a;
  15. let c = a;
  16. }());
  17. console.log(a); // undefined
  18. console.log(b); // undefined
  19. console.log(c); // undefined
  20. // `const` 也是如此

77 不要使用一元自增自减运算符(++--

13.6 Avoid using unary increments and decrements (++, --).
eslint no-plusplus

Why? 根据eslint文档,一元增量和减量语句受到自动分号插入的影响,并且可能会导致应用程序中的值递增或递减的无声错误。 使用num + = 1而不是num ++num ++语句来表达你的值也是更有表现力的。 禁止一元增量和减量语句还会阻止您无意地预增/预减值,这也会导致程序出现意外行为。

  1. // bad
  2. const array = [1, 2, 3];
  3. let num = 1;
  4. num++;
  5. --num;
  6. let sum = 0;
  7. let truthyCount = 0;
  8. for (let i = 0; i < array.length; i++) {
  9. let value = array[i];
  10. sum += value;
  11. if (value) {
  12. truthyCount++;
  13. }
  14. }
  15. // good
  16. const array = [1, 2, 3];
  17. let num = 1;
  18. num += 1;
  19. num -= 1;
  20. const sum = array.reduce((a, b) => a + b, 0);
  21. const truthyCount = array.filter(Boolean).length;

78. 在赋值的时候避免在 = 前/后换行

如果你的赋值语句超出 max-len, 那就用小括号把这个值包起来再换行。
13.7 Avoid linebreaks before or after = in an assignment. If your assignment violates max-len, surround the value in parens.
eslint operator-linebreak.

Why? 在 = 附近换行容易混淆这个赋值语句。

  1. // bad
  2. const foo =
  3. superLongLongLongLongLongLongLongLongFunctionName();
  4. // bad
  5. const foo
  6. = 'superLongLongLongLongLongLongLongLongString';
  7. // good
  8. const foo = (
  9. superLongLongLongLongLongLongLongLongFunctionName()
  10. );
  11. // good
  12. const foo = 'superLongLongLongLongLongLongLongLongString';

79. 不允许有未使用的变量

13.8 Disallow unused variables.
eslint: no-unused-vars

Why? 一个声明了但未使用的变量更像是由于重构未完成产生的错误。这种在代码中出现的变量会使阅读者迷惑。

  1. // bad
  2. var some_unused_var = 42;
  3. // 写了没用
  4. var y = 10;
  5. y = 5;
  6. // 变量改了自己的值,也没有用这个变量
  7. var z = 0;
  8. z = z + 1;
  9. // 参数定义了但未使用
  10. function getX(x, y) {
  11. return x;
  12. }
  13. // good
  14. function getXPlusY(x, y) {
  15. return x + y;
  16. }
  17. var x = 1;
  18. var y = a + 2;
  19. alert(getXPlusY(x, y));
  20. // 'type' 即使没有使用也可以可以被忽略, 因为这个有一个 rest 取值的属性。
  21. // 这是从对象中抽取一个忽略特殊字段的对象的一种形式
  22. var { type, ...coords } = data;
  23. // 'coords' 现在就是一个没有 'type' 属性的 'data' 对象

Hoisting

80. var声明会被提前到他的作用域的最前面,它分配的值还没有提前

constlet被赋予了新的调用概念时效区 —— Temporal Dead Zones (TDZ)。 重要的是要知道为什么 typeof不再安全.
14.1 var declarations get hoisted to the top of their closest enclosing function scope, their assignment does not. const and let declarations are blessed with a new concept called Temporal Dead Zones (TDZ). It’s important to know why typeof is no longer safe.

  1. // 我们知道这个不会工作,假设没有定义全局的notDefined
  2. function example() {
  3. console.log(notDefined); // => throws a ReferenceError
  4. }
  5. // 在你引用的地方之后声明一个变量,他会正常输出是因为变量作用域上升。
  6. // 注意: declaredButNotAssigned的值没有上升
  7. function example() {
  8. console.log(declaredButNotAssigned); // => undefined
  9. var declaredButNotAssigned = true;
  10. }
  11. // 解释器把变量声明提升到作用域最前面,
  12. // 可以重写成如下例子, 二者意义相同
  13. function example() {
  14. let declaredButNotAssigned;
  15. console.log(declaredButNotAssigned); // => undefined
  16. declaredButNotAssigned = true;
  17. }
  18. // 用 const, let就不一样了
  19. function example() {
  20. console.log(declaredButNotAssigned); // => throws a ReferenceError
  21. console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
  22. const declaredButNotAssigned = true;
  23. }

81. 匿名函数表达式提升其变量名,但不提升函数赋值

14.2 Anonymous function expressions hoist their variable name, but not the function assignment.

  1. function example() {
  2. console.log(anonymous); // => undefined
  3. anonymous(); // => TypeError anonymous is not a function
  4. var anonymous = function () {
  5. console.log('anonymous function expression');
  6. };
  7. }

82.已命名函数表达式提升他的变量名,不是函数名或函数体

14.3 Named function expressions hoist the variable name, not the function name or the function body.

  1. function example() {
  2. console.log(named); // => undefined
  3. named(); // => TypeError named is not a function
  4. superPower(); // => ReferenceError superPower is not defined
  5. var named = function superPower() {
  6. console.log('Flying');
  7. };
  8. }
  9. // 函数名和变量名一样是也如此
  10. function example() {
  11. console.log(named); // => undefined
  12. named(); // => TypeError named is not a function
  13. var named = function named() {
  14. console.log('named');
  15. };
  16. }

83. 函数声明则提升了函数名和函数体

14.4 Function declarations hoist their name and the function body.

  1. function example() {
  2. superPower(); // => Flying
  3. function superPower() {
  4. console.log('Flying');
  5. }
  6. }

Comparison Operators & Equality

84. 用 ===!== 而不是 ==!=

15.1 Use === and !== over == and !=.
eslint: eqeqeq

85. 条件语句如’if’语句使用强制`ToBoolean’抽象方法来评估它们的表达式,并且始终遵循以下简单规则:

15.2 Conditional statements such as the if statement evaluate their expression using coercion with the ToBoolean abstract method and always follow these simple rules:

  • Objects 计算成 true
  • Undefined 计算成 false
  • Null 计算成 false
  • Booleans 计算成 the value of the boolean
  • Numbers
    • +0, -0, or NaN 计算成 false
    • 其他 true
  • Strings
    • '' 计算成 false
    • 其他 true
      1. if ([0] && []) {
      2. // true
      3. // 数组(即使是空数组)是对象,对象会计算成true
      4. }

      86. 布尔值用缩写,而字符串和数字要明确比较对象

      15.3 Use shortcuts for booleans, but explicit comparisons for strings and numbers.
      15.4 For more information see Truth Equality and JavaScript by Angus Croll. ```javascript // bad if (isValid === true) { // … }

// good if (isValid) { // … }

// bad if (name) { // … }

// good if (name !== ‘’) { // … }

// bad if (collection.length) { // … }

// good if (collection.length > 0) { // … }

  1. 更多信息请见Angus Croll的[真理、平等和JavaScript —— Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)
  2. <a name="RPZY9"></a>
  3. ### 87. 在`case`和`default`分句里用大括号创建一块包含语法声明的区域(e.g. `let`, `const`, `function`, and `class`)
  4. [15.5](https://github.com/airbnb/javascript#comparison--switch-blocks) Use braces to create blocks in `case` and `default` clauses that contain lexical declarations (e.g. `let`, `const`, `function`, and `class`).<br />eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html).
  5. > Why? 语法声明在整个`switch`的代码块里都可见,但是只有当其被分配后才会初始化,他的初始化时当这个`case`被执行时才产生。 当多个`case`分句试图定义同一个事情时就出问题了
  6. ```javascript
  7. // bad
  8. switch (foo) {
  9. case 1:
  10. let x = 1;
  11. break;
  12. case 2:
  13. const y = 2;
  14. break;
  15. case 3:
  16. function f() {
  17. // ...
  18. }
  19. break;
  20. default:
  21. class C {}
  22. }
  23. // good
  24. switch (foo) {
  25. case 1: {
  26. let x = 1;
  27. break;
  28. }
  29. case 2: {
  30. const y = 2;
  31. break;
  32. }
  33. case 3: {
  34. function f() {
  35. // ...
  36. }
  37. break;
  38. }
  39. case 4:
  40. bar();
  41. break;
  42. default: {
  43. class C {}
  44. }
  45. }

88. 三元表达式不应该嵌套,通常是单行表达式

15.6 Ternaries should not be nested and generally be single line expressions.
eslint rules: no-nested-ternary.

  1. // bad
  2. const foo = maybe1 > maybe2
  3. ? "bar"
  4. : value1 > value2 ? "baz" : null;
  5. // better
  6. const maybeNull = value1 > value2 ? 'baz' : null;
  7. const foo = maybe1 > maybe2
  8. ? 'bar'
  9. : maybeNull;
  10. // best
  11. const maybeNull = value1 > value2 ? 'baz' : null;
  12. const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

89. 避免不需要的三元表达式

15.7 Avoid unneeded ternary statements.
eslint rules: no-unneeded-ternary.

  1. // bad
  2. const foo = a ? a : b;
  3. const bar = c ? true : false;
  4. const baz = c ? false : true;
  5. // good
  6. const foo = a || b;
  7. const bar = !!c;
  8. const baz = !c;

90. 用圆括号来混合这些操作符

只有当标准的算术运算符(+, -, *, & /), 并且它们的优先级显而易见时,可以不用圆括号括起来。
15.8 When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators: +, -, and ** since their precedence is broadly understood. We recommend enclosing / and *in parentheses because their precedence can be ambiguous when they are mixed.
eslint: no-mixed-operators

Why? 这提高了可读性,并且明确了开发者的意图

  1. // bad
  2. const foo = a && b < 0 || c > 0 || d + 1 === 0;
  3. // bad
  4. const bar = a ** b - 5 % d;
  5. // bad
  6. // 别人会陷入(a || b) && c 的迷惑中
  7. if (a || b && c) {
  8. return d;
  9. }
  10. // good
  11. const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
  12. // good
  13. const bar = (a ** b) - (5 % d);
  14. // good
  15. if (a || (b && c)) {
  16. return d;
  17. }
  18. // good
  19. const bar = a + b / c * d;

Blocks

91. 用大括号包裹多行代码块

16.1 Use braces with all multiline blocks.
eslint: nonblock-statement-body-position

  1. // bad
  2. if (test)
  3. return false;
  4. // good
  5. if (test) return false;
  6. // good
  7. if (test) {
  8. return false;
  9. }
  10. // bad
  11. function foo() { return false; }
  12. // good
  13. function bar() {
  14. return false;
  15. }

92. if表达式的elseif的关闭大括号在一行

16.2 If you’re using multiline blocks with if and else, put else on the same line as your if block’s closing brace.
eslint: brace-style

  1. // bad
  2. if (test) {
  3. thing1();
  4. thing2();
  5. }
  6. else {
  7. thing3();
  8. }
  9. // good
  10. if (test) {
  11. thing1();
  12. thing2();
  13. } else {
  14. thing3();
  15. }

93. 如果 if 语句中总是需要用 return 返回, 那后续的 else 就不需要写了

if 块中包含 return, 它后面的 else if 块中也包含了 return, 这个时候就可以把 return 分到多个 if 语句块中。
16.3 If an if block always executes a return statement, the subsequent else block is unnecessary. A return in an else if block following an if block that contains a return can be separated into multiple if blocks.
eslint: no-else-return

  1. // bad
  2. function foo() {
  3. if (x) {
  4. return x;
  5. } else {
  6. return y;
  7. }
  8. }
  9. // bad
  10. function cats() {
  11. if (x) {
  12. return x;
  13. } else if (y) {
  14. return y;
  15. }
  16. }
  17. // bad
  18. function dogs() {
  19. if (x) {
  20. return x;
  21. } else {
  22. if (y) {
  23. return y;
  24. }
  25. }
  26. }
  27. // good
  28. function foo() {
  29. if (x) {
  30. return x;
  31. }
  32. return y;
  33. }
  34. // good
  35. function cats() {
  36. if (x) {
  37. return x;
  38. }
  39. if (y) {
  40. return y;
  41. }
  42. }
  43. // good
  44. function dogs(x) {
  45. if (x) {
  46. if (z) {
  47. return y;
  48. }
  49. } else {
  50. return z;
  51. }
  52. }

Control Statements

94. 当你的控制语句(if, while 等)太长或者超过最大长度限制的时候, 把每一个(组)判断条件放在单独一行里。 逻辑操作符放在行首。

17.1 In case your control statement (if, while etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line.

Why? 把逻辑操作符放在行首是让操作符的对齐方式和链式函数保持一致。这提高了可读性,也让复杂逻辑更容易看清楚。

  1. // bad
  2. if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
  3. thing1();
  4. }
  5. // bad
  6. if (foo === 123 &&
  7. bar === 'abc') {
  8. thing1();
  9. }
  10. // bad
  11. if (foo === 123
  12. && bar === 'abc') {
  13. thing1();
  14. }
  15. // bad
  16. if (
  17. foo === 123 &&
  18. bar === 'abc'
  19. ) {
  20. thing1();
  21. }
  22. // good
  23. if (
  24. foo === 123
  25. && bar === 'abc'
  26. ) {
  27. thing1();
  28. }
  29. // good
  30. if (
  31. (foo === 123 || bar === 'abc')
  32. && doesItLookGoodWhenItBecomesThatLong()
  33. && isThisReallyHappening()
  34. ) {
  35. thing1();
  36. }
  37. // good
  38. if (foo === 123 && bar === 'abc') {
  39. thing1();
  40. }

95. 不要用选择操作符代替控制语句

17.2 Don’t use selection operators in place of control statements.

  1. // bad
  2. !isRunning && startRunning();
  3. // good
  4. if (!isRunning) {
  5. startRunning();
  6. }

Comments

96. 多行注释用 /** ... */

18.1 Use /** ... */ for multiline comments.

  1. // bad
  2. // make() returns a new element
  3. // based on the passed in tag name
  4. //
  5. // @param {String} tag
  6. // @return {Element} element
  7. function make(tag) {
  8. // ...
  9. return element;
  10. }
  11. // good
  12. /**
  13. * make() returns a new element
  14. * based on the passed-in tag name
  15. */
  16. function make(tag) {
  17. // ...
  18. return element;
  19. }

97. 单行注释用//,将单行注释放在被注释区域上面。如果注释不是在第一行,那么注释前面就空一行

18.2 Use // for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.

  1. // bad
  2. const active = true; // is current tab
  3. // good
  4. // is current tab
  5. const active = true;
  6. // bad
  7. function getType() {
  8. console.log('fetching type...');
  9. // set the default type to 'no type'
  10. const type = this._type || 'no type';
  11. return type;
  12. }
  13. // good
  14. function getType() {
  15. console.log('fetching type...');
  16. // set the default type to 'no type'
  17. const type = this._type || 'no type';
  18. return type;
  19. }
  20. // also good
  21. function getType() {
  22. // set the default type to 'no type'
  23. const type = this._type || 'no type';
  24. return type;
  25. }

98. 所有注释开头空一格,方便阅读

18.3 Start all comments with a space to make it easier to read.
eslint: spaced-comment

  1. // bad
  2. //is current tab
  3. const active = true;
  4. // good
  5. // is current tab
  6. const active = true;
  7. // bad
  8. /**
  9. *make() returns a new element
  10. *based on the passed-in tag name
  11. */
  12. function make(tag) {
  13. // ...
  14. return element;
  15. }
  16. // good
  17. /**
  18. * make() returns a new element
  19. * based on the passed-in tag name
  20. */
  21. function make(tag) {
  22. // ...
  23. return element;
  24. }

99. 在你的注释前使用FIXME'或TODO’前缀

这有助于其他开发人员快速理解你指出的需要重新访问的问题, 或者您建议需要实现的问题的解决方案。 这些不同于常规注释,因为它们是可操作的。 动作是FIXME: - 需要计算出来TODO: - 需要实现
18.4 Prefixing your comments with FIXME or TODO helps other developers quickly understand if you’re pointing out a problem that needs to be revisited, or if you’re suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are FIXME: -- need to figure this out or TODO: -- need to implement.

100. 用// FIXME:给问题做注释

18.5 Use // FIXME: to annotate problems.

  1. class Calculator extends Abacus {
  2. constructor() {
  3. super();
  4. // FIXME: shouldn't use a global here
  5. total = 0;
  6. }
  7. }

101. 用// TODO:去注释问题的解决方案

18.6 Use // TODO: to annotate solutions to problems.

  1. class Calculator extends Abacus {
  2. constructor() {
  3. super();
  4. // TODO: total should be configurable by an options param
  5. this.total = 0;
  6. }
  7. }

Whitespace

10. tab用两个空格

19.1 Use soft tabs (space character) set to 2 spaces.
eslint: indent

  1. // bad
  2. function foo() {
  3. ∙∙∙∙const name;
  4. }
  5. // bad
  6. function bar() {
  7. const name;
  8. }
  9. // good
  10. function baz() {
  11. ∙∙const name;
  12. }

102. 在大括号前空一格

19.2 Place 1 space before the leading brace.
eslint: space-before-blocks

  1. // bad
  2. function test(){
  3. console.log('test');
  4. }
  5. // good
  6. function test() {
  7. console.log('test');
  8. }
  9. // bad
  10. dog.set('attr',{
  11. age: '1 year',
  12. breed: 'Bernese Mountain Dog',
  13. });
  14. // good
  15. dog.set('attr', {
  16. age: '1 year',
  17. breed: 'Bernese Mountain Dog',
  18. });

104. 在控制语句(if, while 等)的圆括号前空一格。在函数调用和定义时,参数列表和函数名之间不空格。

19.3 Place 1 space before the opening parenthesis in control statements (if, while etc.). Place no space between the argument list and the function name in function calls and declarations.
eslint: keyword-spacing

  1. // bad
  2. if(isJedi) {
  3. fight ();
  4. }
  5. // good
  6. if (isJedi) {
  7. fight();
  8. }
  9. // bad
  10. function fight () {
  11. console.log ('Swooosh!');
  12. }
  13. // good
  14. function fight() {
  15. console.log('Swooosh!');
  16. }

105. 用空格来隔开运算符

19.4 Set off operators with spaces.
eslint: space-infix-ops

  1. // bad
  2. const x=y+5;
  3. // good
  4. const x = y + 5;

106. 文件结尾空一行

19.5 End files with a single newline character.
eslint: eol-last

  1. // bad
  2. import { es6 } from './AirbnbStyleGuide';
  3. // ...
  4. export default es6;
  1. // bad
  2. import { es6 } from './AirbnbStyleGuide';
  3. // ...
  4. export default es6;↵
  1. // good
  2. import { es6 } from './AirbnbStyleGuide';
  3. // ...
  4. export default es6;↵

107. 当出现长的方法链(>2个)时用缩进,用点开头强调该行是一个方法调用,而不是一个新的语句

19.6 Use indentation when making long method chains (more than 2 method chains). Use a leading dot, which emphasizes that the line is a method call, not a new statement.
eslint: newline-per-chained-call no-whitespace-before-property

  1. // bad
  2. $('#items').find('.selected').highlight().end().find('.open').updateCount();
  3. // bad
  4. $('#items').
  5. find('.selected').
  6. highlight().
  7. end().
  8. find('.open').
  9. updateCount();
  10. // good
  11. $('#items')
  12. .find('.selected')
  13. .highlight()
  14. .end()
  15. .find('.open')
  16. .updateCount();
  17. // bad
  18. const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
  19. .attr('width', (radius + margin) * 2).append('svg:g')
  20. .attr('transform', `translate(${radius + margin},${radius + margin})`)
  21. .call(tron.led);
  22. // good
  23. const leds = stage.selectAll('.led')
  24. .data(data)
  25. .enter().append('svg:svg')
  26. .classed('led', true)
  27. .attr('width', (radius + margin) * 2)
  28. .append('svg:g')
  29. .attr('transform', `translate(${radius + margin},${radius + margin})`)
  30. .call(tron.led);
  31. // good
  32. const leds = stage.selectAll('.led').data(data);

108. 在一个代码块后下一条语句前空一行

19.7 Leave a blank line after blocks and before the next statement.

  1. // bad
  2. if (foo) {
  3. return bar;
  4. }
  5. return baz;
  6. // good
  7. if (foo) {
  8. return bar;
  9. }
  10. return baz;
  11. // bad
  12. const obj = {
  13. foo() {
  14. },
  15. bar() {
  16. },
  17. };
  18. return obj;
  19. // good
  20. const obj = {
  21. foo() {
  22. },
  23. bar() {
  24. },
  25. };
  26. return obj;
  27. // bad
  28. const arr = [
  29. function foo() {
  30. },
  31. function bar() {
  32. },
  33. ];
  34. return arr;
  35. // good
  36. const arr = [
  37. function foo() {
  38. },
  39. function bar() {
  40. },
  41. ];
  42. return arr;

109. 不要用空白行填充块

19.8 Do not pad your blocks with blank lines.
eslint: padded-blocks

  1. // bad
  2. function bar() {
  3. console.log(foo);
  4. }
  5. // also bad
  6. if (baz) {
  7. console.log(qux);
  8. } else {
  9. console.log(foo);
  10. }
  11. // good
  12. function bar() {
  13. console.log(foo);
  14. }
  15. // good
  16. if (baz) {
  17. console.log(qux);
  18. } else {
  19. console.log(foo);
  20. }

110. 不要在代码之间使用多个空白行填充

19.9 Do not use multiple blank lines to pad your code.
eslint: no-multiple-empty-lines

  1. // bad
  2. class Person {
  3. constructor(fullName, email, birthday) {
  4. this.fullName = fullName;
  5. this.email = email;
  6. this.setAge(birthday);
  7. }
  8. setAge(birthday) {
  9. const today = new Date();
  10. const age = this.getAge(today, birthday);
  11. this.age = age;
  12. }
  13. getAge(today, birthday) {
  14. // ..
  15. }
  16. }
  17. // good
  18. class Person {
  19. constructor(fullName, email, birthday) {
  20. this.fullName = fullName;
  21. this.email = email;
  22. this.setAge(birthday);
  23. }
  24. setAge(birthday) {
  25. const today = new Date();
  26. const age = getAge(today, birthday);
  27. this.age = age;
  28. }
  29. getAge(today, birthday) {
  30. // ..
  31. }
  32. }

111. 圆括号里不要加空格

19.10 Do not add spaces inside parentheses.
eslint: space-in-parens

  1. // bad
  2. function bar( foo ) {
  3. return foo;
  4. }
  5. // good
  6. function bar(foo) {
  7. return foo;
  8. }
  9. // bad
  10. if ( foo ) {
  11. console.log(foo);
  12. }
  13. // good
  14. if (foo) {
  15. console.log(foo);
  16. }

112. 方括号里不要加空格

19.11 Do not add spaces inside brackets.
eslint: array-bracket-spacing

  1. // bad
  2. const foo = [ 1, 2, 3 ];
  3. console.log(foo[ 0 ]);
  4. // good, 逗号分隔符还是要空格的
  5. const foo = [1, 2, 3];
  6. console.log(foo[0]);

113. 花括号里加空格

19.12 Add spaces inside curly braces.
eslint: object-curly-spacing

  1. // bad
  2. const foo = {clark: 'kent'};
  3. // good
  4. const foo = { clark: 'kent' };

114. 避免一行代码超过100个字符(包含空格)。

注意: 对于上面——strings—line-length,长字符串不受此规则限制,不应分解。
19.13 Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per above, long strings are exempt from this rule, and should not be broken up.
eslint: max-len

Why? 这样确保可读性和可维护性

  1. // bad
  2. const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
  3. // bad
  4. $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
  5. // good
  6. const foo = jsonData
  7. && jsonData.foo
  8. && jsonData.foo.bar
  9. && jsonData.foo.bar.baz
  10. && jsonData.foo.bar.baz.quux
  11. && jsonData.foo.bar.baz.quux.xyzzy;
  12. // good
  13. $.ajax({
  14. method: 'POST',
  15. url: 'https://airbnb.com/',
  16. data: { name: 'John' },
  17. })
  18. .done(() => console.log('Congratulations!'))
  19. .fail(() => console.log('You have failed this city.'));

115. 作为语句的花括号内也要加空格 —— { 后和 } 前都需要空格

19.14 Require consistent spacing inside an open block token and the next token on the same line. This rule also enforces consistent spacing inside a close block token and previous token on the same line.
eslint: block-spacing

  1. // bad
  2. function foo() {return true;}
  3. if (foo) { bar = 0;}
  4. // good
  5. function foo() { return true; }
  6. if (foo) { bar = 0; }

116. , 前不要空格, , 后需要空格。 eslint: comma-spacing

19.15 Avoid spaces before commas and require a space after commas.

  1. // bad
  2. var foo = 1,bar = 2;
  3. var arr = [1 , 2];
  4. // good
  5. var foo = 1, bar = 2;
  6. var arr = [1, 2];

117. 计算属性内要空格

19.16 Enforce spacing inside of computed property brackets.
参考上述花括号和中括号的规则。
eslint: computed-property-spacing

  1. // bad
  2. obj[foo ]
  3. obj[ 'foo']
  4. var x = {[ b ]: a}
  5. obj[foo[ bar ]]
  6. // good
  7. obj[foo]
  8. obj['foo']
  9. var x = { [b]: a }
  10. obj[foo[bar]]

118. 调用函数时,函数名和小括号之间不要空格

19.17 Avoid spaces between functions and their invocations.
eslint: func-call-spacing

  1. // bad
  2. func ();
  3. func
  4. ();
  5. // good
  6. func();

119. 在对象的字面量属性中, key value 之间要有空格

19.18 Enforce spacing between keys and values in object literal properties.
eslint: key-spacing

  1. // bad
  2. var obj = { "foo" : 42 };
  3. var obj2 = { "foo":42 };
  4. // good
  5. var obj = { "foo": 42 };

120. 行末不要空格

19.19 Avoid trailing spaces at the end of lines.
eslint: no-trailing-spaces

121. 避免出现多个空行,在文件末尾只允许空一行

19.20 Avoid multiple empty lines, only allow one newline at the end of files, and avoid a newline at the beginning of files.
eslint: no-multiple-empty-lines

  1. // bad
  2. var x = 1;
  3. var y = 2;
  4. // good
  5. var x = 1;
  6. var y = 2;

Commas

122. 不要前置逗号

20.1 Leading commas: Nope.
eslint: comma-style

  1. // bad
  2. const story = [
  3. once
  4. , upon
  5. , aTime
  6. ];
  7. // good
  8. const story = [
  9. once,
  10. upon,
  11. aTime,
  12. ];
  13. // bad
  14. const hero = {
  15. firstName: 'Ada'
  16. , lastName: 'Lovelace'
  17. , birthYear: 1815
  18. , superPower: 'computers'
  19. };
  20. // good
  21. const hero = {
  22. firstName: 'Ada',
  23. lastName: 'Lovelace',
  24. birthYear: 1815,
  25. superPower: 'computers',
  26. };

123. 需要额外结尾逗号

20.2 Additional trailing comma
eslint: comma-dangle

Why? 这导致git diffs更清洁。 此外,像Babel这样的转换器会删除转换代码中的额外的逗号,这意味着你不必担心旧版浏览器中的结尾逗号问题

  1. // bad - 没有结尾逗号的 git diff
  2. const hero = {
  3. firstName: 'Florence',
  4. - lastName: 'Nightingale'
  5. + lastName: 'Nightingale',
  6. + inventorOf: ['coxcomb chart', 'modern nursing']
  7. };
  8. // good - 有结尾逗号的 git diff
  9. const hero = {
  10. firstName: 'Florence',
  11. lastName: 'Nightingale',
  12. + inventorOf: ['coxcomb chart', 'modern nursing'],
  13. };
  1. // bad
  2. const hero = {
  3. firstName: 'Dana',
  4. lastName: 'Scully'
  5. };
  6. const heroes = [
  7. 'Batman',
  8. 'Superman'
  9. ];
  10. // good
  11. const hero = {
  12. firstName: 'Dana',
  13. lastName: 'Scully',
  14. };
  15. const heroes = [
  16. 'Batman',
  17. 'Superman',
  18. ];
  19. // bad
  20. function createHero(
  21. firstName,
  22. lastName,
  23. inventorOf
  24. ) {
  25. // does nothing
  26. }
  27. // good
  28. function createHero(
  29. firstName,
  30. lastName,
  31. inventorOf,
  32. ) {
  33. // does nothing
  34. }
  35. // good (note that a comma must not appear after a "rest" element)
  36. function createHero(
  37. firstName,
  38. lastName,
  39. inventorOf,
  40. ...heroArgs
  41. ) {
  42. // does nothing
  43. }
  44. // bad
  45. createHero(
  46. firstName,
  47. lastName,
  48. inventorOf
  49. );
  50. // good
  51. createHero(
  52. firstName,
  53. lastName,
  54. inventorOf,
  55. );
  56. // good (note that a comma must not appear after a "rest" element)
  57. createHero(
  58. firstName,
  59. lastName,
  60. inventorOf,
  61. ...heroArgs
  62. )

Semicolons

124. 以分号结束语句

eslint: semi

Why? 当 JavaScript 遇到没有分号结尾的一行,它会执行自动插入分号 Automatic Semicolon Insertion这一规则来决定行末是否加分号。如果JavaScript在你的断行里错误的插入了分号,就会出现一些古怪的行为。当新的功能加到JavaScript里后, 这些规则会变得更复杂难懂。显示的结束语句,并通过配置代码检查去捕获没有带分号的地方可以帮助你防止这种错误。

  1. // bad
  2. (function () {
  3. const name = 'Skywalker'
  4. return name
  5. })()
  6. // good
  7. (function () {
  8. const name = 'Skywalker';
  9. return name;
  10. }());
  11. // good, 行首加分号,避免文件被连接到一起时立即执行函数被当做变量来执行。
  12. ;(() => {
  13. const name = 'Skywalker';
  14. return name;
  15. }());

Type Casting & Coercion

125. 在语句开始执行强制类型转换。

22.1 Perform type coercion at the beginning of the statement.

126. 使用正确的方案转换获得Strings对象

eslint: no-new-wrappers

  1. // => this.reviewScore = 9;
  2. // bad
  3. const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
  4. // bad
  5. const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
  6. // bad
  7. const totalScore = this.reviewScore.toString(); // 不保证返回string
  8. // good
  9. const totalScore = String(this.reviewScore);

127. 用 Number 做类型转换,parseInt转换string常需要带上基数

22.3 Numbers: Use Number for type casting and parseInt always with a radix for parsing strings.
eslint: radix

  1. const inputValue = '4';
  2. // bad
  3. const val = new Number(inputValue);
  4. // bad
  5. const val = +inputValue;
  6. // bad
  7. const val = inputValue >> 0;
  8. // bad
  9. const val = parseInt(inputValue);
  10. // good
  11. const val = Number(inputValue);
  12. // good
  13. const val = parseInt(inputValue, 10);

128. 要在注释中解释为什么要用移位运算和你在做什么

无论你做什么狂野的事,比如由于 parseInt 是你的性能瓶颈导致你一定要用移位运算。 请说明这个是因为性能原因
22.4 If for whatever reason you are doing something wild and parseInt is your bottleneck and need to use Bitshift for performance reasons, leave a comment explaining why and what you’re doing.

  1. // good
  2. /**
  3. * parseInt是代码运行慢的原因
  4. * 用Bitshifting将字符串转成数字使代码运行效率大幅增长
  5. */
  6. const val = inputValue >> 0;

129. 注意: 用移位运算要小心

数字使用64-位表示的,但移位运算常常返回的是32为整形source)。移位运算对大于32位的整数会导致意外行为。Discussion. 最大的32位整数是 2,147,483,647:
22.5 Note: Be careful when using bitshift operations. Numbers are represented as 64-bit values, but bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Discussion. Largest signed 32-bit Int is 2,147,483,647:

  1. 2147483647 >> 0 //=> 2147483647
  2. 2147483648 >> 0 //=> -2147483648
  3. 2147483649 >> 0 //=> -2147483647

130. 使用正确的方案转换获得布尔值

  1. const age = 0;
  2. // bad
  3. const hasAge = new Boolean(age);
  4. // good
  5. const hasAge = Boolean(age);
  6. // best
  7. const hasAge = !!age;

Naming Conventions

131. 避免用一个字母命名,让你的命名可描述

23.1 Avoid single letter names. Be descriptive with your naming.
eslint: id-length

  1. // bad
  2. function q() {
  3. // ...
  4. }
  5. // good
  6. function query() {
  7. // ...
  8. }

132. 用小驼峰式命名你的对象、函数、实例

23.2 Use camelCase when naming objects, functions, and instances.
eslint: camelcase

  1. // bad
  2. const OBJEcttsssss = {};
  3. const this_is_my_object = {};
  4. function c() {}
  5. // good
  6. const thisIsMyObject = {};
  7. function thisIsMyFunction() {}

133. 用大驼峰式命名类

23.3 Use PascalCase only when naming constructors or classes.
eslint: new-cap

  1. // bad
  2. function user(options) {
  3. this.name = options.name;
  4. }
  5. const bad = new user({
  6. name: 'nope',
  7. });
  8. // good
  9. class User {
  10. constructor(options) {
  11. this.name = options.name;
  12. }
  13. }
  14. const good = new User({
  15. name: 'yup',
  16. });

134. 不要用前置或后置下划线

23.4 Do not use trailing or leading underscores. eslint: no-underscore-dangle
eslint: no-underscore-dangle

Why? JavaScript 没有私有属性或私有方法的概念。尽管前置下划线通常的概念上意味着“private”,事实上,这些属性是完全公有的,因此这部分也是你的API的内容。这一概念可能会导致开发者误以为更改这个不会导致崩溃或者不需要测试。 如果你想要什么东西变成“private”,那就不要让它在这里出现。

  1. // bad
  2. this.__firstName__ = 'Panda';
  3. this.firstName_ = 'Panda';
  4. this._firstName = 'Panda';
  5. // good
  6. this.firstName = 'Panda';

135. 不要保存引用this, 用箭头函数或函数绑定——Function#bind

23.5 Don’t save references to this. Use arrow functions or Function#bind.

  1. // bad
  2. function foo() {
  3. const self = this;
  4. return function () {
  5. console.log(self);
  6. };
  7. }
  8. // bad
  9. function foo() {
  10. const that = this;
  11. return function () {
  12. console.log(that);
  13. };
  14. }
  15. // good
  16. function foo() {
  17. return () => {
  18. console.log(this);
  19. };
  20. }

136. export default导出模块A,则这个文件名也叫A.*, import 时候的参数也叫A,大小写完全一致

23.6 A base filename should exactly match the name of its default export.

  1. // file 1 contents
  2. class CheckBox {
  3. // ...
  4. }
  5. export default CheckBox;
  6. // file 2 contents
  7. export default function fortyTwo() { return 42; }
  8. // file 3 contents
  9. export default function insideDirectory() {}
  10. // in some other file
  11. // bad
  12. import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
  13. import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
  14. import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
  15. // bad
  16. import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
  17. import forty_two from './forty_two'; // snake_case import/filename, camelCase export
  18. import inside_directory from './inside_directory'; // snake_case import, camelCase export
  19. import index from './inside_directory/index'; // requiring the index file explicitly
  20. import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
  21. // good
  22. import CheckBox from './CheckBox'; // PascalCase export/import/filename
  23. import fortyTwo from './fortyTwo'; // camelCase export/import/filename
  24. import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
  25. // ^ supports both insideDirectory.js and insideDirectory/index.js

137. 当你export-default一个函数时,函数名用小驼峰,文件名需要和函数名一致

23.7 Use camelCase when you export-default a function. Your filename should be identical to your function’s name.

  1. function makeStyleGuide() {
  2. // ...
  3. }
  4. export default makeStyleGuide;

138. 当你export一个结构体/类/单例/函数库/对象 时用大驼峰

23.8 Use PascalCase when you export a constructor / class / singleton / function library / bare object.

  1. const AirbnbStyleGuide = {
  2. es6: {
  3. }
  4. };
  5. export default AirbnbStyleGuide;

139. 简称和缩写应该全部大写或全部小写

23.9 Acronyms and initialisms should always be all uppercased, or all lowercased.

Why? 名字都是给人读的,不是为了适应电脑的算法的。

  1. // bad
  2. import SmsContainer from './containers/SmsContainer';
  3. // bad
  4. const HttpRequests = [
  5. // ...
  6. ];
  7. // good
  8. import SMSContainer from './containers/SMSContainer';
  9. // good
  10. const HTTPRequests = [
  11. // ...
  12. ];
  13. // also good
  14. const httpRequests = [
  15. // ...
  16. ];
  17. // best
  18. import TextMessageContainer from './containers/TextMessageContainer';
  19. // best
  20. const requests = [
  21. // ...
  22. ];

140. 可以用全大写字母设置静态变量,他需要满足三个条件

  1. 导出变量
  2. const 定义的, 保证不能被改变
  3. 这个变量是可信的,他的子属性都是不能被改变的

23.10 You may optionally uppercase a constant only if it (1) is exported, (2) is a const (it can not be reassigned), and (3) the programmer can trust it (and its nested properties) to never change.

Why? 这是一个附加工具,帮助开发者去辨识一个变量是不是不可变的。

  • 对于所有的 const 变量呢? —— 这个是不必要的。大写变量不应该在同一个文件里定义并使用, 它只能用来作为导出变量。 赞同!
  • 那导出的对象呢? —— 大写变量处在export的最高级(e.g. EXPORTED_OBJECT.key) 并且他包含的所有子属性都是不可变的。 ```javascript // bad const PRIVATE_VARIABLE = ‘should not be unnecessarily uppercased within a file’;

// bad export const THING_TO_BE_CHANGED = ‘should obviously not be uppercased’;

// bad export let REASSIGNABLE_VARIABLE = ‘do not use let with uppercase variables’;

// —-

// 允许但不够语义化 export const apiKey = ‘SOMEKEY’;

// 在大多数情况下更好 export const API_KEY = ‘SOMEKEY’;

// —-

// bad - 不必要的大写键,没有增加任何语义 export const MAPPING = { KEY: ‘value’ };

// good export const MAPPING = { key: ‘value’ };

  1. <a name="kJY2B"></a>
  2. ## Accessors
  3. <a name="8dMZ1"></a>
  4. ### 141. 不需要使用属性的访问器函数。
  5. [24.1](https://github.com/airbnb/javascript#accessors--not-required) Accessor functions for properties are not required.
  6. <a name="1QPWW"></a>
  7. ### 142. 不要使用JavaScript的getters/setters
  8. 因为他们会产生副作用,并且难以测试、维护和理解。相反的,你可以用 getVal()和setVal('hello')去创造你自己的accessor函数。<br />[24.2](https://github.com/airbnb/javascript#accessors--no-getters-setters) Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use `getVal()` and `setVal('hello')`.
  9. ```javascript
  10. // bad
  11. class Dragon {
  12. get age() {
  13. // ...
  14. }
  15. set age(value) {
  16. // ...
  17. }
  18. }
  19. // good
  20. class Dragon {
  21. getAge() {
  22. // ...
  23. }
  24. setAge(value) {
  25. // ...
  26. }
  27. }

143. 如果属性/方法是boolean, 用 isVal()hasVal()

24.3 If the property/method is a boolean, use isVal() or hasVal().

  1. // bad
  2. if (!dragon.age()) {
  3. return false;
  4. }
  5. // good
  6. if (!dragon.hasAge()) {
  7. return false;
  8. }

144. 用get()和set()函数是可以的,但是要一起用

24.4 It’s okay to create get() and set() functions, but be consistent.

  1. class Jedi {
  2. constructor(options = {}) {
  3. const lightsaber = options.lightsaber || 'blue';
  4. this.set('lightsaber', lightsaber);
  5. }
  6. set(key, val) {
  7. this[key] = val;
  8. }
  9. get(key) {
  10. return this[key];
  11. }
  12. }

Events

145. 当需要向事件传递数据时(不论是DOM事件还是像Backbone事件的很多属性),通过对象的literal(又称哈希或散列)而不是原始值。

这使得后续的贡献者(程序员)向这个事件装载更多的数据时不用去找或者更新每个处理器。
25.1 When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass an object literal (also known as a “hash”) instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event.
例如:

  1. // bad
  2. $(this).trigger('listingUpdated', listing.id);
  3. // ...
  4. $(this).on('listingUpdated', (e, listingID) => {
  5. // do something with listingID
  6. });

更好的做法:

  1. // good
  2. $(this).trigger('listingUpdated', { listingID: listing.id });
  3. // ...
  4. $(this).on('listingUpdated', (e, data) => {
  5. // do something with data.listingID
  6. });

jQuery

146. jQuery对象用$变量表示

26.1 Prefix jQuery object variables with a $.

  1. // bad
  2. const sidebar = $('.sidebar');
  3. // good
  4. const $sidebar = $('.sidebar');
  5. // good
  6. const $sidebarBtn = $('.sidebar-btn');

147. 暂存jQuery查找

26.2 Cache jQuery lookups.

  1. // bad
  2. function setSidebar() {
  3. $('.sidebar').hide();
  4. // ...
  5. $('.sidebar').css({
  6. 'background-color': 'pink'
  7. });
  8. }
  9. // good
  10. function setSidebar() {
  11. const $sidebar = $('.sidebar');
  12. $sidebar.hide();
  13. // ...
  14. $sidebar.css({
  15. 'background-color': 'pink'
  16. });
  17. }

148. DOM查找用层叠式$('.sidebar ul') 或 父节点 > 子节点 $('.sidebar > ul')

26.3 For DOM queries use Cascading $('.sidebar ul') or parent > child $('.sidebar > ul').
jsPerf

149. 用jQuery对象查询作用域的find方法查询

26.4 Use find with scoped jQuery object queries.

  1. // bad
  2. $('ul', '.sidebar').hide();
  3. // bad
  4. $('.sidebar').find('ul').hide();
  5. // good
  6. $('.sidebar ul').hide();
  7. // good
  8. $('.sidebar > ul').hide();
  9. // good
  10. $sidebar.find('ul').hide();

Standard Library

150. 用 Number.isNaN 代替全局的 isNaN

29.1 Use Number.isNaN instead of global isNaN. eslint: no-restricted-globals
eslint: no-restricted-globals

Why? 全局 isNaN 强制把非数字转成数字, 然后对于任何强转后为 NaN 的变量都返回 true 如果你想用这个功能,就显式的用它。

  1. // bad
  2. isNaN('1.2'); // false
  3. isNaN('1.2.3'); // true
  4. // good
  5. Number.isNaN('1.2.3'); // false
  6. Number.isNaN(Number('1.2.3')); // true

151. Number.isFinite 代替 isFinite

29.2 Use Number.isFinite instead of global isFinite.
eslint: no-restricted-globals

Why? 理由同上,会把一个非数字变量强转成数字,然后做判断。

  1. // bad
  2. isFinite('2e3'); // true
  3. // good
  4. Number.isFinite('2e3'); // false
  5. Number.isFinite(parseInt('2e3', 10)); // true

Testing

152. 测试是非常重要的

  1. function foo() {
  2. return true;
  3. }

153. No, but seriously

  • 无论用那个测试框架,你都需要写测试。
  • 尽量去写很多小而美的纯函数,减少突变的发生
  • 小心 stub 和 mock —— 这会让你的测试变得脆弱。
  • 在 Airbnb 首选 mochatape 偶尔被用来测试一些小的,独立的模块。
  • 100%测试覆盖率是我们努力的目标,即便实际上很少达到。
  • 每当你修了一个bug, 都要写一个回归测试。 一个bug修复了,没有回归测试,很可能以后会再次出问题。