一、 编码风格
1. 块
- 多行代码块必须使用大括号包裹
// bad
if (isArray) return false
// good
if (isArray) {
return false
}
2. 空格
- 块的左大括号前面有个空格
// bad
function test(){}
if(isArray){}
// good
if (isArray) {
return false
}
function test() {
}
- 小括号中括号内部两侧无空格,大括号内部两侧有空格
// bad
if ( isArray ) {}
const arr=[ 1,2,3 ]
const obj={a:1}
// good
if (isArray) {}
const arr = [1, 2, 3]
const obj = { a: 1 }
- 运算符两测有空格(除了一元运算符)
// bad
const x=y+1
const isRight=result===0?true:false
// good
const x = y + 1
const isRight = result === 0 ? true : false
// bad 一元运算符与操作对象中间不应该有空格
const x = ! y
// good
const x = !y
- 定义对象字面量时, key, value 之间有且只有一个空格,不允许所谓的「水平对齐」
// bad
{
a: 1,
b : 2
}
// good
{
a: 1,
b: 2
}
3. 空行
- 块的开始和结束不能是空行
// bad
function test() {
return false
}
// good
function test() {
return false
}
- 在块的末尾和语句间加一个空行
// bad
if (foo) {
return bar;
}
return baz;
// good
if (foo) {
return bar;
}
return baz;
// bad
const obj = {
foo() {
},
bar() {
},
};
return obj;
// good
const obj = {
foo() {
},
bar() {
},
};
return obj;
二、 语言特性
1. 变量声明
- 使用
let和const去声明变量
从ES6开始, 可以使用
let和const去声明变量,块级作用域不会污染全局命名空间,不要使用var
应优先使用
const,只有当变量会被重新赋值时才使用let:
// bad
var test = 1;
var test;
// good
let test = 1;
// bad
let flag = true
if (flag) {
// ...
}
//如果变量在后期会发生变化使用let 否则使用const
// good
const flag = true;
if (flag) {
// ...
}
- 将
let和const归类在一起写,提高代码的整洁性
// bad
let a = 1;
const b = 2;
let c = 3;
const d = 4;
// good
const b = 2;
const d = 4;
let a = 1;
let c = 3;
2. 原始类型
- 不要使用
new Number/String/Boolean去声明变量,这样会导致变量为Object类型,可能引起bug
const num = new Number(0);
const str = new String('');
const bool = new Boolean(false);
if (num && str && bool) {
//... 会执行 表示 三个值都为true
}
let a = 0;
let b = '';
let c = false;
if (a || b || c) {
// 不会执行 表示三个值都为false
}
- 类型转换
【数字】转换使用
Number或parseInt
const str = '1';
// bad
const num = +str;
const num = str >> 0;
const num = new Number(str);
// good
const num = Number(str);
const num = parseInt(str, 10)
【字符串】转换使用
String
const num = 1;
// bad
const str = new String(num);
const str = num + '';
const str = num.toString();
// good
const str = String(num);
【布尔值】使用
!!
const age = 0;
// bad
const bool = new Boolean(age);
const bool = Boolean(age);
// good
const bool = !!age
3. 数组
- 不要使用
new Array()和Array()去创建变量
// bad
const a = new Array(1, 2, 3);
const b = Array(1, 2, 3);
// good
const a = [1, 2, 3];
const b = new Array(100); // 构建长度为500的空数组
- 使用扩展运算符
...处理数组
数组复制:
const arr = [1, 2, 3];
// bad
const arr2 = arr.map(item => item);
// good
const arr2 = [...arr];
数组拼接:
// bad
const array = [1, 2, 3].concat([4, 5]);
// good
const array = [1, 2, 3, ...[4, 5]];
用
...替代apply:
const arr = [1, 2, 3, 4, 5];
// bad
Math.max.apply(Math, arr);
// good
Math.max(...arr);
使用解构获取数据元素
const arr = [1, 2, 3, 4, 5];
// bad
const A = arr[0];
const B = arr[3];
// good
const [A, , , B] = arr
4. 对象
- 使用字面量创建对象
// bad
const obj = new Object();
// good
const obj = {};
- 使用对象的简写形式
const value = 'test';
// bad
const obj = {
value: value,
addValue: function (value) {
return value + 'add'
},
}
// good
const obj = {
value,
addValue(value) {
return value + 'add'
},
}
- 对象的属性名不要用引号包裹,除非包含特殊字符
// bad
const obj = {
'a': 1,
'b': 2,
'data-a': 3,
'data-b': 4.
}
// good
const obj = {
a: 1,
b: 2,
'data-a': 3,
'data-b': 4.
}
- 优先使用
.访问对象的属性, 访问动态属性名或者包含特殊字符时可用[]
const obj = {
a: 1,
'data-a': 2,
}
// bad
const a = obj['a']
// good
const a = obj.a;
const dataA = obj['data-a'];
const b = 'b'
obj[b] = 3;
- 使用扩展运算符
...处理对象, 代替Object.assgin()
const test = { a: 1, b: 2 };
// very bad
const test2 = Object.assgin(test, { c: 3 }) // 会影响到原数据
test.a = 5;
console.log(test2.a) // 5
// bad
const test2 = Object.assgin({}, test, { c: 3 })
// good
const test2 = {
...test,
c: 3,
}
- 使用解构获取对象属性
const obj = {
a: 1,
b: 2,
}
// bad
const a = obj.a;
const b = obj.b;
// good
const { a, b} = obj;
5. 函数
- 不要使用
Function去创建函数
// bad
const sum = new Function('a', 'b', 'return a + b');
// good
const sum = (a, b) => a + b
- 不要再块中使用函数声明
// bad
if (true) {
function test() {
console.log('test');
}
}
test() //在函数外部也可以去使用 会引起误解
// good
if (true) {
const test = () => {
console.log('test')
}
}
test() // ReferenceError: test is not defined
- 使用箭头函数替代匿名函数
// bad
[1, 2, 3].map(function(item) {
return item
});
// good
[1, 2, 3].map(item => item);
- 不要使用
arguments对象
ES6提供了rest操作符...
// bad
function test(a, b) {
const args = Array.prototype.slice.call(arguments, test.length);
console.log(args)
}
// good
function foo(a, b, ...args) {
console.log(args);
}
foo(1, 2, 3, 4); // => [3, 4]
- 使用默认参数语法(有默认参数的需要放在参数列表的最后)
// bad
const multiple = (a, b) => {
a = a || 0;
b = b || 0;
return a * b;
};
// good
const multiple = (a = 0, b = 0) =>{
return a * b
}
- 不要修改函数的参数
const obj = { key: 0 }
// bad
const test = obj => {
obj.key = 1
};
test(obj);
console.log(obj) // { key: 1 };
// good
const test = obj => {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
- 更不要给函数参数重新赋值,这可能导致意外的行为和内核优化问题:
// bad
function foo(bar, baz) {
if (!baz) {
bar = 1;
}
}
// good
function foo(bar, baz) {
let qux = bar;
if (!baz) {
qux = 1;
}
}
- 优先使用 JS 提供的高阶函数进行迭代运算。
如使用 map() / every() / filter() / find() / findIndex() / reduce() / some() / ... 来迭代数组,使用 Object.keys() / Object.values() / Object.entries() 方法来迭代对象
const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
console.log(sum); // => 15;
// good
let sum = 0;
numbers.forEach((num) => { sum += num; });
console.log(sum); // => 15;
// best
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // => 15;
// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
// good
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
// best
const increasedByOne = numbers.map(num => num + 1);
6. 模块
- 使用 ES6 modules 而非其他非标准的模块系统
// bad
const React = require('react');
module.exports = React.Component;
// good
import React, { Component } from 'react';
export default Component;
- 不要用多个 import 引入同一模块
// bad
import React from 'react';
import { Component } from 'react';
// good
import React, { Component } from 'react';
- import 语句需要放到模块的最上方
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
bar.init();
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
bar.init();
- import 语句的排序
- 先
import第三方模块,再import自己工程里的模块- 先
import绝对路径,再import相对路径
7. 操作符
- 使用严格相等运算符
const id = '123';
// bad == 会转换类型比较(最好使用===)
if (id == 123) {
// ...
}
// good
if (Number(id) === 123) {
// ...
}
- 不要使用 void 运算符
在很老版本中
undefined值时可变的,因此使用void语句一般是用来得到一个undefined值,但在新版本中,上面的问题已经不复存在
// bad
const foo = void 0;
// good
const foo = undefined;
- 避免嵌套的三元表达式
// bad
const test = a > b ? a : b > c ? b : c
// good
const foo = b > c ? b : c
const test = a > b ? a : foo
- 避免不必要的三元表达式
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
- 混合使用多种操作符时,用小括号包裹分组
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
8. 注释
- 注释行上方需要有一个空行(除非注释行上方是一个块的顶部) 注释后面跟一个空格
// bad
if (isDisable) {
const a = 1;
// .....
return a
}
// good
if (isDisable) {
const a = 1;
// .....
return a
}
- 文档类注释使用 jsdoc 规范
/**
* Book类,代表一个书本.
* @constructor
* @param {string} title - 书本的标题.
* @param {string} author - 书本的作者.
*/
function Book(title, author) {
this.title = title;
this.author = author;
}
Book.prototype = {
/**
* 获取书本的标题
* @returns {string|*}
*/
getTitle() {
return this.title;
},
/**
* 设置书本的页数
* @param pageNum {number} 页数
*/
setPageNum(pageNum) {
this.pageNum = pageNum;
},
};
9. 命名
- 使用小驼峰命名原始类型,对象,函数,实例。
// bad
const this_is_my_string = 'foo';
const this_is_my_object = {};
function this_is_my_function() {
// ...
}
// good
const thisIsMyString = 'foo';
const thisIsMyObject = {};
function thisisMyFunction() {
// ...
}
- 使用大驼峰命名类和构造函数
// bad
function user(options) {
this.name = options.name;
}
// good
function User(options) {
this.name = options.name
}
- 全部大写字母&单词间用下划线分割的命名模式 用于声明常量
如果仅仅是单个文件去用的常量,命名按照之前的规范就可以,在很多文件下都需要去使用的常量,需要按照此规范进行
// bad
const PRE_VALUE = '一个常量'
export let PRE_VALUE = '一个常量'
// good
export const PRE_VALUE = '一个常量'