原文: https://github.com/airbnb/javascript,依据本人习惯做了部分修改
- 1.1 常量使用关键字
const
。
为什么?这可以确保该变量不能二次赋值,可以减少bug及晦涩代码的。
```javascript
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
```
- 1.2 如果需要使用可变的引用,使用
let
,不要使用var
。
为什么?
let
在函数内具有块级作用域,var
没有。
```javascript
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
```
-
1.3 注意
let
和const
都具有块级作用域。// 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
-
2.1 使用简写的属性。
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker }; // good const obj = { lukeSkywalker };
-
2.2 简写属性放在开头并分组。
const anakinSkywalker = 'Anakin Skywalker' const lukeSkywalker = 'Luke Skywalker' // bad const obj = { episodeOne: 1, twoJedisWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, } // good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJedisWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, }
-
3.1 使用扩展运算符
...
复制数组。// bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]
-
3.2 使用Array.from将伪数组转成数组。
const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);
-
4.1 使用对象解构。
// bad function getFullName(user) { const firstName = user.firstName const lastName = user.lastName return `${firstName} ${lastName}` } // good function getFullName(obj) { const { firstName, lastName } = obj return `${firstName} ${lastName}` } // best function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}` }
-
4.2 使用数组解构。
const arr = [1, 2, 3, 4] // bad const first = arr[0] const second = arr[1] // good const [first, second] = arr
-
4.3 使用对象解构一次返回多个值。
// bad function processInput(input) { // 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, right } = processInput(input);
-
5.1 使用模板替代字符串拼加。
// bad function sayHi(name) { return 'How are you, ' + name + '?'; } // bad function sayHi(name) { return ['How are you, ', name, '?'].join(); } // good function sayHi(name) { return `How are you, ${name}?`; }
- 6.1 使用扩展运算符替代
arguments
。
为什么?
...
具有arguments的所有功能,并且它是一个真正的数组,arguments
则是一个伪数组。
```javascript
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
```
-
6.2 多个参数有默认值时,使用默认参数特性。
// really bad function handleThings(opts) { opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... }
-
6.3 注意避免默认参数的副作用。
var b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3
- 7.1 当需要使用函数表达式(或匿名函数)时,应使用箭头函数。
为什么? 箭头函数的上下文通常是你想要的, 另外它很简洁。
```javascript
// bad
[1, 2, 3].map(function (x) {
return x * x;
});
// good
[1, 2, 3].map((x) => {
return x * x;
});
```
- 7.2 如函数在一行且只有一个参数,可以省略括号和圆括号,并使用隐式返回。否则,加括号并使用
return
声明。
为什么?语法糖,它可读性更好,且可以把多个函数链接在一起。
```javascript
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].reduce((total, n) => {
return total + n;
}, 0);
```
-
8.1 使用
class
定义类,避免使用函数及其原型。// bad function Queue(contents = []) { this._queue = [...contents]; } Queue.prototype.pop = function() { const value = this._queue[0]; this._queue.splice(0, 1); return value; } // good class Queue { constructor(contents = []) { this._queue = [...contents]; } pop() { const value = this._queue[0]; this._queue.splice(0, 1); return value; } }
-
8.2 使用
extends
实现继承。// 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]; } }
-
8.3 方法内可以返回
this
实现链式调用。// bad Jedi.prototype.jump = function() { this.jumping = true; return true; }; Jedi.prototype.setHeight = function(height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // good class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump().setHeight(20);
-
8.4 可以重写类的toString方法,只要确保它可用且无副作用。
class Jedi { contructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return 'Jedi - ${this.getName()}'; } }
-
9.1 经常使用 (
import
/export
) 在非标准的模块系统中,你可以迁移到你想要的模块系统。// bad const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // ok import AirbnbStyleGuide from './AirbnbStyleGuide'; export default AirbnbStyleGuide.es6; // best import { es6 } from './AirbnbStyleGuide'; export default es6;
-
9.2 不要使用通配符导入模块。
// bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide';
-
9.3 不要导入后立即导出模块。
为什么? 虽然写在一行简洁,但分开有一个清晰的进口和出口,让事情保持一致。
```javascript
// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
```
- 10.1 不要使用迭代器,使用 JavaScript 的高阶函数如
map()
和reduce()
替代for-of
。
为什么? 这能确保不变性,处理纯函数的返回值相对容易且无副作用。
```javascript
const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;
// good
let sum = 0;
numbers.forEach((num) => sum += num);
sum === 15;
// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
```
- 10.2 目前不要使用生成器。
Why? They don't transpile well to ES5.
-
11.1 总是使用
const
声明变量,使用var
可能会导致全局变量,我们要避免污染全局命名空间。// bad superPower = new SuperPower(); // good const superPower = new SuperPower();
-
11.2 每一个变量都使用一个
const
声明。为什么? 这种方式新增一个变量更容易,不用费劲的去交换
;
和,
, 又或有时遗漏了。// bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z';
-
11.3 把所有使用
const
和let
的变量分组。
为什么? 当变量随后需要赋值,但它又依赖前一个赋值的变量时将很有帮助。
```javascript
// bad
let i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
```
- 11.4 在需要的时候给变量赋值,但注意把变量放在合理的位置。
为什么?
let
和const
具有块级作用域而非函数级作用域。
```javascript
// good
function() {
test();
console.log('doing stuff..');
//..other stuff..
const name = getName();
if (name === 'test') {
return false;
}
return name;
}
// bad - unnessary function call
function(hasName) {
const name = getName();
if (!hasName) {
return false;
}
this.setFirstName(name);
return true;
}
// good
function(hasName) {
if (!hasName) {
return false;
}
const name = getName();
this.setFirstName(name);
return true;
}
```
-
12.1
const
andlet
声明的有一个新概念成为 暂时性死区(TDZ)。这对于理解 为什么typeof不再安全 非常重要。function example() { console.log(notDefined); // => throws a ReferenceError } function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; } // using const and let function example() { console.log(declaredButNotAssigned); // => throws a ReferenceError console.log(typeof declaredButNotAssigned); // => throws a ReferenceError const declaredButNotAssigned = true; }
-
12.2 匿名函数表达式的变量名发生变量提升,它不是一个真正的函数(undefined),不能当函数去调用。
function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function() { console.log('anonymous function expression'); }; }
-
12.3 具名函数表达式的变量名也会发生变量提升,无论是变量名还是具名函数名都不能当成真正的函数去调用。
function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); } }
-
12.4 函数声明会提升变量名和函数自己,作用域内在声明前可以调用。
function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
-
更多JavaScript作用域和变量提升 by Ben Cherry。
- Draft ECMA 2015 (ES6) Spec
- ExploringJS
- ES6 Compatibility Table
- Comprehensive Overview of ES6 Features
The MIT License (MIT)
Copyright (c) 2015 snandy
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.