块级作用域
let取代var
ES6提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而let没有副作用。
‘use srict’
if( true ) {
console.log( x ); //ReferenceError
let x = ‘hello’;
}
上面的代码如果用var代替let,那么console.log那一行就不会报错,而是输出undefined,因为变量声明会提升到代码块的头部。这违反了变量先声明后使用的原则。
所以,建议不要使用var命令,而是使用let命令取代。
全局常量和线程安全
在let和const之间,建议优先使用const,尤其是在全局环境中,不应该使用变量,只应设置常量。
const优于let有以下几个原因:
- const可以提醒阅读程序的人,这个变量不应该改变。
- const比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算。
- JavaScript编译器会对const进行优化,所以多使用const有利于程序的运行效率。
//bad
var a = 1, b = 2, c = 3;
//good
const a = 1;
const b = 2;
const c = 3;
//best
const [ a, b, c] = [ 1, 2, 3 ];
字符串
静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用双引号。
//bad
const a = “foobar”;
const b = ‘foo’ + a + ‘bar’;
//acceptable
const c = `foobar`;
//good
const a = ‘foobar’;
const b = `foo${ a }bar`;
const c = ‘foobar’;
解构赋值
使用数组成员对变量赋值时,优先使用解构赋值。
const arr = [ 1, 2, 3, 4];
//bad
const first = arr[ 0 ];
const second arr[ 1 ];
//good
const [ first, second ] = arr;
函数的参数如果是对象的成员,优先使用解构赋值。
//bad
function getFullName( user ) {
const firstName = user.firstName;
const lastName = user.lastName;
}
//good
function getFullName( obj ) {
const { firstName , lastName } = obj;
}
//best
function getFullName( { firstName , lastName }) {
}
如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。
//bad
function processInput( input ) {
return [ left, right, top, bottom];
}
//good
function processInput( input ) {
return { left, right, top, bottom};
}
const { left, right} = processInput( input );
对象
单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
//bad
const a = { k1, k2, k3, k4, };
const b = {
K1: v1,
K2: v2
};
//good
const a = { k1, k2, k3, k4 };
const b = {
K1: v1,
K2: v2,
};
对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。
//bad
const a = { };
a.x = 3;
// if reshape unavoidable
const a = { };
Object.assign( a, { x: 3 } );
//good
const a = { x: null };
a.x = 3;
如果对象属性名时动态的,可以在创造对象的时候使用属性表达式定义。
//bad
const obj = {
id: 5,
name : ‘San Francisco’,
};
obj[getKey( ‘enable’ ) ] = true;
//good
const obj = {
id: 5,
name : ‘San Francisco’,
[getKey( ‘enable’ ) ] = true,
};
数组
使用扩展运算符(...)复制数组
//bad
const len = items.length;
const itemsCopy = [];
let i;
for( i = 0; i < len; i++ ) {
itemsCopy[ i ] = items[ i ];
}
//good
const itemsCopy = [ ...items ];
使用Array.form方法将类似数组的对象转为数组。
const foo = document.querySelectorAll(‘.foo’);
const nodes = Array.from(foo);
函数
立即执行函数可以写成箭头函数的形式。
(() => {
console.log(‘hello word’);
})();
那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了this。
//bad
[ 1, 2, 3].map(function (x) {
return x*x;
});
//good
[ 1, 2, 3].map( (x) => {
return x*x;
});
//best
[ 1, 2, 3].map( x => x*x);
箭头函数取代Function.prototype.bind,不应再用self/_this/that绑定this。
//bad
const self = this;
const boundMethod = function( ...params) {
return method.apply( self, params);
}
//acceptable
const boundMethod = method.bind( this);
//best
const boundMethod = ( ...params) => method.apply( this, params
);
简单的、单行的、不会复用的函数,建议采用箭头函数。
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。
//bad
function divide( a, b, option = false) {}
//good
function divide( a, b, { option = false } = {}) {}
使用默认值语法设置函数参数的默认值。
//bad
function handleThings( opts) {
opts = opts || { };
}
//good
function handleThings( opts = { } ) {
// ...
}
不要在函数体内使用arguments变量,使用rest运算符(. . .)代替。因为rest运算符可以显式表明我们想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组。
//bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join(‘ ’);
}
//good
function concatenateAll( . . .args ) {
return args.join(‘ ’);
}
Map结构
注意区分Object和Map,只有模拟实体对象时才使用Object。如果只是需要key:value的数据结构,则使用Map。因为Map有内建的遍历机制。
let map = new Map(arr);
for( let key of map.keys()) {
console.log(key);
}
for( let item of map.entries()) {
console.log(item[ 0 ], item[ 1 ]);
}
Class
总是用Class取代需要prototype的操作。因为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
}
}
ESLint
ESLint是一个语法规则和代码风格的检查工具,可用于保证写出语法正确、风格统一的代码。
首先,安装ESLint
$ npm i -g eslint
然后,安装Airbnb语法规则。
$ npm i -g eslint-config-airbnb
最后,在项目的根目录下新建.eslintrc文件,配置ESLint。
{
“extends”: “eslint-config-airbnb”
}
现在就可以检查当前项目的代码是否符合预计的规则。
index.js文件代码如下。
var unusued = ‘I have no purpose’;
function greet() {
var message = ‘Hello,Word!’;
alert(message);
}
greet();
使用ESLint检查这个文件。
$ eslint index.js
index.js
1:5 error unusued is defined but never used no-unused-vars
4:5 error Expected indentation of 2 characters but found 4 indent
5:5 error Expected indentation of 2 characters but found 4 indent
3 problems (3 errors, 0 warnings)
上面的代码说明,源文件有3个错误,一个是定义了变量,却没有使用,另外两个行首缩进为4个空格,我不是两个空格。