ES6提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用
var命令存在变量提升效用,let命令没有这个问题
建议不再使用var命令,而是使用let命令取代
1
2
3
4
5
"use strict";
if(true) {
console.log(x);// ReferenceError
letx ='hello';
}
上面代码如果使用var替代let,console.log那一行就不会报错,而是会输出undefined,因为变量声明提
升到代码块的头部。这违反了变量先声明后使用的原则
在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。这符合函数式编程思想,有利于将来的分布式运算。
1
2
3
4
5
6
7
8
9
10
// bad
vara =1, b =2, c =3;
// good
consta =1;
constb =2;
constc =3;
// best
const[a, b, c] = [1,2,3];
const声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改
变量值所导致的错误
所有的函数都应该设置为常量
let表示的变量,只应出现在单线程运行的代码中,不能是多线程共享的,这样有利于保证线程安全
V8引擎只在严格模式之下,支持let和const。结合前两点,这实际上意味着,将来所有的编程都是针对严
格模式的。
静态字符串一律使用单引号,不使用双引号。动态字符串使用反引号
1
2
3
4
5
6
7
8
9
// bad
consta ="foobar";
constb ='foo'+ a +'bar';
// good
consta ='foobar';
constb =`foo${a}bar`;
constc ='foobar';
使用数组成员对变量赋值,优先使用解构赋值
1
2
3
4
5
6
7
8
constarr = [1,2,3,4];
// bad
constfirst = arr[0];
constsecond = arr[1];
// good
const[first, second] = arr;
函数的参数如果是对象的成员,优先使用解构赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// bad
functiongetFullName(user){
constfirstName = user.firstName;
constlastName = user.lastName;
}
// good
functiongetFullName(obj){
const{ firstName, lastName } = obj;
}
// best
functiongetFullName({ firstName, lastName }){
}
如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序
1
2
3
4
5
6
7
8
9
10
// bad
functionprocessInput(input){
return[left, right, top, bottom];
}
// good
functionprocessInput(input){
return{ left, right, top, bottom };
}
const{ left, right } = processInput(input);
单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾
1
2
3
4
5
6
7
8
9
10
11
12
13
// bad
consta = {k1: v1,k2: v2, };
constb = {
k1: v1,
k2: v2
};
// good
consta = {k1: v1,k2: v2 };
constb = {
k1: v1,
k2: v2,
};
对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法
1
2
3
4
5
6
7
8
9
10
11
// bad
consta = {};
a.x =3;
// if reshape unavoidable
consta = {};
Object.assign(a, {x:3});
// good
consta = {x:null};
a.x =3
如果对象的属性名是动态的,可以在创造对象的时候,使用属性表达式定义
1
2
3
4
5
6
7
8
9
10
11
12
13
// bad
constobj = {
id:5,
name:'San Francisco',
};
obj[getKey('enabled')] =true;'
// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
上面代码中,对象obj的最后一个属性名,需要计算得到。这时最好采用属性表达式,在新建obj的时候,将该属性与其他属性定义在一起。这样一来,所有属性就在一个地方定义了
另外,对象的属性和方法,尽量采用简洁表达法,这样易于描述和书写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ar ref ='some value';
// bad
constatom = {
ref: ref,
value:1,
addValue:function(value){
returnatom.value + value;
},
};
// good
constatom = {
ref,
value:1,
addValue(value) {
returnatom.value + value;
},
};
使用扩展运算符(...)拷贝数组
1
2
3
4
5
6
7
8
9
10
// bad
constlen = items.length;
constitemsCopy = [];
leti;
for(i =0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
constitemsCopy = [...items];
使用Array.from方法,将类似数组的对象转为数组
1
2
constfoo =document.querySelectorAll('.foo');
constnodes =Array.from(foo);
立即执行函数可以写成箭头函数的形式
1
2
3
(()=>{
console.log('Welcome to the Internet.');
})();
那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了this
1
2
3
4
5
6
7
8
9
// bad
[1,2,3].map(function(x){
returnx * x;
});
// good
[1,2,3].map((x) =>{
returnx * x;
});
箭头函数取代Function.prototype.bind,不应再用self/_this/that绑定this
1
2
3
4
5
6
7
8
9
10
11
// bad
constself =this;
constboundMethod =function(...params){
returnmethod.apply(self, params);
}
// acceptable
constboundMethod = method.bind(this);
// best
constboundMethod =(...params) =>method.apply(this, params);
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数
1
2
3
4
5
6
7
// bad
functiondivide(a, b, option = false){
}
// good
functiondivide(a, b, { option = false } = {}){
}
不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest运算符显式表明你想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组
1
2
3
4
5
6
7
8
9
10
// bad
functionconcatenateAll(){
constargs =Array.prototype.slice.call(arguments);
returnargs.join('');
}
// good
functionconcatenateAll(...args){
returnargs.join('');
}
使用默认值语法设置函数参数的默认值
1
2
3
4
5
6
7
8
9
// bad
functionhandleThings(opts){
opts = opts || {};
}
// good
functionhandleThings(opts = {}){
// ...
}
注意区分Object和Map,只有模拟实体对象时,才使用Object。如果只是需要key:value的数据结构,使用Map。因为Map有内建的遍历机制
1
2
3
4
5
6
7
8
9
10
letmap =newMap(arr);
for(letkeyofmap.keys()) {
console.log(key);
}
for(letvalueofmap.values()) {
console.log(value);
}
for(letitemofmap.entries()) {
console.log(item[0], item[1]);
}
总是用class,取代需要prototype操作。因为class的写法更简洁,更易于理解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// bad
functionQueue(contents = []){
this._queue = [...contents];
}
Queue.prototype.pop =function(){
constvalue =this._queue[0];
this._queue.splice(0,1);
returnvalue;
}
// good
classQueue{
constructor(contents = []) {
this._queue = [...contents];
}
pop() {
constvalue =this._queue[0];
this._queue.splice(0,1);
returnvalue;
}
}
使用extends实现继承,因为这样更简单,不会有破坏instanceof运算的危险
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad
constinherits =require('inherits');
functionPeekableQueue(contents){
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek =function(){
returnthis._queue[0];
}
// good
classPeekableQueueextendsQueue{
peek() {
returnthis._queue[0];
}
}
Module语法是JavaScript模块的标准写法,坚持使用这种写法。使用import取代require
1
2
3
4
5
6
7
// bad
constmoduleA =require('moduleA');
constfunc1 = moduleA.func1;
constfunc2 = moduleA.func2;
// good
import{ func1, func2 }from'moduleA';
使用export取代module.exports
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// commonJS的写法
varReact =require('react');
varBreadcrumbs = React.createClass({
render() {
return;
}
});
module.exports = Breadcrumbs;
// ES6的写法
import React from 'react';
const Breadcrumbs = React.createClass({
render() {
return;
}
});
export default Breadcrumbs
不要在模块输入中使用通配符。因为这样可以确保你的模块之中,有一个默认输出(export default)
1
2
3
4
5
// bad
import*asmyObject'./importModule';
// good
importmyObjectfrom'./importModule';
如果模块默认输出一个函数,函数名的首字母应该小写
1
2
3
functionmakeStyleGuide(){
}
exportdefaultmakeStyleGuide;
如果模块默认输出一个对象,对象名的首字母应该大写
1
2
3
4
5
constStyleGuide = {
es6: {
}
};
exportdefaultStyleGuide;