JavaScript语言编程规范(ES6)

本篇博文参考Airbnb规范制定,有错误或不当请您务必指出

代码风格

导出的默认函数使用驼峰命名、文件名与函数完全一致。

1
2
3
4
function makeStyleGuide(){

}
export default makeStyleGuide;

导出单例、函数库、空对象时使用帕斯卡式命名(帕斯卡式命名法是在命名的时候将首字母大写, 如: DisplayInfo)。

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

类型和变量

变星必须显式声明作用域

  • var => 用于声明全局变量或函数级变量
  • let => 用于声明块级的局部变量
  • const => 声明块级域的只读局部变量。
1
2
3
4
5
6
7
8
9
const names = [];
names.push('john');
console.log(names);
// 变量名字在内存中的指针不能够改变, 但是指向这个变量的值可能改变。
name = [] // ERROR
{
let name;
name = 'tom'
}

在ES6中, const代表一个值的“常量索引”, 换句话说, 变量名字在内存中的指针不能够改变, 但是指向这个变量的值可能改变。

尽量对所有的引用使用const, 不要使用var。 如果你一定需要使用可变动的引用, 使用let代替var。

不好

1
2
3
4
5
var a=1;
var count=1;
if(true){
count+=1
}

1
2
3
4
5
const a=1;
let count=1;
if(true){
count+=1
}

说明:const和let的作用域更小, 写代码更容易控制。 const可确保无法对引用重新赋值, const引用的指针不变, 重新赋值会报错, 避免不小心的重新赋值给覆盖了。

将所有的const和let分组,并隔行

不好

1
2
3
4
let i;
const items=[];
let d;
const flag=true;

1
2
3
4
5
const flag=true;
const items=[];

let i;
let d;

在需要的地方给变量 赋值, 但请把它们放在一个合理的位置。

说明:let和const是作用域而不是函数作用域, 不用担心变量定义会被前移导致问题, 把变量的赋值和调用代码放在一起会使逻辑更加清晰, 可读性更好。

不好

1
2
3
4
5
6
7
8
function(name){
const name=getName();
if(name){
return false;
}
this.setName(name);
return true;
}

1
2
3
4
5
6
7
8
function(name){
if(name){
return false;
}
const name=getName();
this.setName(name);
return true;
}

对象和引用

创建有动态属性名的对象时, 尽量在一个地方定义对象的所有属性。

不好

1
2
3
4
5
const obj={
id:'aadadadaffaxvv',
name:'zhangsan',
}
obj[getKey('enabled')]=true;

1
2
3
4
5
6
const obj={
id:'aadadadaffaxvv',
name:'zhangsan',
obj[getKey('enabled')]:true,
}

使用对象方法的简写。

说明:ES6中, 对象字面量被增强了, 写法更加简洁与灵活, 同时在定义对象的时候, 能够做的事件更多了。

不好

1
2
3
4
5
6
const atom = {
value: 1,
addValue:function(value) {
return atom.value + value;
}
};

1
2
3
4
5
6
7
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
}
};

使用对象属性的简写

不好

1
2
3
const obj = {
a: a,
};

1
2
3
const obj = {
a,
};

数组

使用扩展运算符 … 复制数组

扩展运算符可以减少赋值语句的使用, 或者减少通过下标访问数组或对象的方式, 使用代码更加简洁优雅, 可读性更佳。

不好

1
2
3
4
5
6
7
const len = items.length;
const itemsCopy = [];

let i;
for(i=0; i<len; i++) {
itemsCopy[i] = items[i];
}

1
const itemsCopy = [...items];

属性

使用 . 来访问对象的属性, 只有属性是动态的时候使用 []。

1
2
const key=getKey();
const props=obj[key];

逗号、分号

逗号写在行尾, 并且增加结尾的逗号。

不好

1
2
3
4
const obj = {
firstName: 'Dana',
lastName: 'Scally'
};

1
2
3
4
const obj = {
firstName: 'Dana',
lastName: Scally',
};

使用分号, 以分号作为语句的结束符。

1
2
3
;(()=>{
const name='a';
})();

函数

使用函数声明代替函数表达式

说明:因为函数声明是可命名的, 所以他们在调用栈中更容易被识别。 此外, 函数声明会把整个函数提升, 而函数表达式只会把函数引用的变量名提升。 这条规则使得箭头函数可以取代函数表达式。

不好

1
2
3
const function = foo(){

}

1
2
3
function foo(){

}

不要使用arguments, 可以选择rest语法 … 替代。

说明:使用 … 能明确你要传入的参数, 另外, rest语法参数是一个真正的数组, 而arguments是一个类数组。

不好

1
2
3
4
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join(' ');
}

1
2
3
function concatenateAll(...args) {
return args.join(' ');
}

直接给函数的参数指定默认值, 不要使用一个变化的函数参数。

1
2
3
function handle(opts={}){

}

直接给函数参数赋值时, 需要避免副作用。

不好

1
2
3
4
5
6
var b=1;
function count(a=b++){

}
count();//1
count();//2

代码块

使用大括号包裹所有的多行代码块。

不好

1
2
3
4
if(true) 
return false;

function (){ return false }

1
2
3
4
5
6
7
8
if(true) return false;
if(true) {
return false;
}

function (){
return false
}
  1. 如果通过if和else使用多行代码块, 把else放在if代码块关闭括号的同一行。

不好

1
2
3
4
5
6
if(arg) {
return arg;
}
else {
return false;
}

1
2
3
4
5
if(arg) {
return arg;
} else {
return false;
}

模块

代码中总是使用ES6标准的模块(import/export)方式, 而不是使用非标准的模块加载器。

不好

1
2
const AirbnbStyleGuide=require('./AirbnbStyleGuide');
moudle.exports=AirbnbStyleGuide.es6;

1
2
import AirbnbStyleGuide from '/AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

更好

1
2
import { es6 } from '/AirbnbStyleGuide';
export default es6;

不要使用通配符 *的import 。

说明:这样可以确保被import的模块只有一个默认的export项。

不好

1
import * as AirbnbStyleGuide from '/AirbnbStyleGuide';

1
import AirbnbStyleGuide from '/AirbnbStyleGuide';

不要从import中直接export.

不好

1
export {es6 as defaults} from './ablier.js'; 

1
2
import {es6} from './ablier.js';
export default es6;

如果你的文件只输出一个类, 那你的文件名必须和类名完全保持一致。

1
import CheckBox from './CheckBox';

箭头函数

别保存this的引用, 使用箭头函数或Function.bind。

说明:箭头函数提供了更简洁的语法, 并且箭头函数中的this对象指向是不变的, this绑定到定义时所在的对象。 通常情况下, 这是我们想要的, 有很好的代码可读性, 而保存this对象的使用方式, 会让开发人员搞混。

不好

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo(){
const self=this;
return function(){
console.log(self)
}
}

function foo(){
const that=this;
return function(){
console.log(that)
}
}

1
2
3
4
5
function foo(){
return () =>{
console.log(this)
}
}

当你必须使用函数表达式(或传递一个匿名函数)时, 使用箭头函数。

说明:箭头函数创造了一个新的this执行环境。 通常情况下, 能满足你的需求, 而且这样的写更为简洁。 如果你有一个相当复杂的函数, 那么可以把逻辑部分转移到一个函数声明上。

不好

1
2
3
[1, 2, 3].map(function(x) {
return x * x;
});

1
2
3
[1, 2, 3].map( (x) => {
return x * x;
});

如果一个函数适用一行写出并且只有一个参数, 那就把花括号、圆括号和return都省略掉, 如果不是, 那就不要省略。

1
2
3
4
5
6
7
[1, 2, 3].map( x => x * x;);


[1, 2, 3].reduce((total,n)=>{
return total+n;
}, 0)

构造器

采用class关键字定义类

不好

1
2
3
4
5
6
7
8
9
function Queue(contents=[]{
this._queue=[...contents];
})
Queue.prototype.pop=function(){
const value=this._queue[0];
this.queue.splice(0,1);
return value;
}

1
2
3
4
5
6
7
8
9
10
class Queue{
constructor(contents=[]){
this._queue=[...contents];
}
pop(){
const value=this._queue[0];
this.queue.splice(0,1);
return value;
}
}

采用extends关键字实现继承。

说明:因为extends是内建的继承方式, 并不会破坏instanceof原型检查。

不好

1
2
3
4
5
6
7
8
9
const inherits=require('inherits');
function peekableQueue(contents){
Queue.apply(this, contents)
}
inherits(peekableQueue, Queue);
peekableQueue.prototype.peek=function(){
return this._queue[0];
}

1
2
3
4
5
class peekableQueue extends Queue{
peek(){
return this._queue[0];
}
}

解构

使用解构存取和使用多属性对象。

说明:ES6允许按照一定的模式, 从数组和对象中提取值、对变量进行赋值, 这称之为解构, 解构赋值避免了临时变量或对象, 给JavaScript书写带来了很大的便利性, 同时也提高了代码的可读性。

不好

1
2
3
4
5
function getFullName (user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}

1
2
3
function getFullName ({firstName, lastName}) {
return `${firstName} ${lastName}`;
}

将数组成员赋值给变量时, 使用数组解析。

不好

1
2
3
const arr = [1,2,3]
const first=arr[0];
const second=arr[1];

1
const [first, second]=arr;

需要回传多个值时, 使用对象解构, 而不是数组解构。

说明:对象解构在增加属性或改变排序时, 无需改变调用时的位置。

不好

1
2
3
4
5
6
function input(inputType){
return [left, right, top, bottom]
}

// 调用时候考虑回调函数顺序
const [left, _, top]=input(inputType);

1
2
// 调用时候只选择需要的数据
const [left, top]=input(inputType);

String

长度超过80的字符串应该使用字符串连接换行。

构建字符串时, 使用字符串模板而不是字符串连接。

1
2
3
function sayHi(name) {
return `How are you, ${name}`;
}

数组遍历采用for/of, 对象遍历采用for/in。

不好

1
2
3
4
5
let arr = [1, 2, 3, 4];
let sum = 0;
for (let num in arr) {
sum += num;
}

1
2
3
4
5
let arr = [1, 2, 3, 4];
let sum = 0;
for (let num of arr) {
sum += num;
}

存取器

属性的存取函数不是必须的, 如果需要存储函数使用get方法和set方法。 如果属性是布尔值, 存取函数是isVal()或hasVal()。

不好

1
2
3
4
5
dragon.age();
dragon.age(25);
if (!dragon.age()) {
return false;
}

1
2
3
4
5
dragon.getAge();
dragon.setAge(25);
if (!dragon.getAge()) {
return false;
}

创建get()和set()函数要保持一致。

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