References
- 所有的赋值都用
const
,避免使用var
Why? 因为这个确保你不会改变你的初始值,重复引用会导致bug和代码难以理解
- 如果一定要对参数重新赋值,就用
let
, 而不是var
Why? 因为let是块级作用域,而var是函数级作用域
- let / const 都是块级作用域
Objects
- 使用字面量创建对象
// bad
const item = new Object();// good
const item = {};
- 用对象方法简写
// bad
const atom = {
value: 1,addValue: function (value) { return atom.value + value; }, };
// good
const atom = {
value: 1,// 对象的方法
addValue(value) { return atom.value + value; }, };
- 用属性值缩写
const lukeSkywalker = ‘Luke Skywalker’;
// bad
const obj = { lukeSkywalker: lukeSkywalker, };// good
const obj = { lukeSkywalker, };
- 不要直接调用Object.prototype上的方法,如
hasOwnProperty
,propertyIsEnumerable
,isPrototypeOf
Why? 在一些有问题的对象上, 这些方法可能会被屏蔽掉 - 如:{ hasOwnProperty: false } -
或这是一个空对象Object.create(null)
// bad
console.log(object.hasOwnProperty(key));// good
console.log(Object.prototype.hasOwnProperty.call(object,
key));// best
const has = Object.prototype.hasOwnProperty; // 在模块作用内做一次缓存 /*
or */ import has from ‘has’; // https://www.npmjs.com/package/has //
…
console.log(has.call(object, key));
- 对象浅拷贝时,更推荐使用拓展运算符[…],而不是
Object.assign
。获取对象指定的几个属性时,用对象的rest解构运算符较好
// very bad
const original = { a: 1, b: 2 }; const copy =
Object.assign(original, { c: 3 }); // this mutatesoriginal
ಠ_ಠ
delete copy.a; // so does this// bad
const original = { a: 1, b: 2 }; const copy = Object.assign({},
original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }// good
es6扩展运算符 … const original = { a: 1, b: 2 }; // 浅拷贝 const
copy = { …original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }// rest 赋值运算符
const { a, …noA } = copy; // noA => { b: 2, c: 3 }
Arrays
- 用字面量赋值
// bad
const items = new Array();// good
const items = [];
- 用拓展运算符做数组浅拷贝
// bad
const len = items.length; const itemsCopy = []; let i;for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; }
// good
const itemsCopy = […items];
- 用…运算符而不是
Array.from
来将一个可迭代的对象转换成数组
const foo = document.querySelectorAll(’.foo’);
// good
const nodes = Array.from(foo);// best
const nodes = […foo];
Destructuring
- 用对象的解构赋值来获取和使用对象某个或多个属性值
// bad
function getFullName(user) { const firstName =
user.firstName; const lastName = user.lastName;return
${firstName} ${lastName}
; }// good
function getFullName(user) { const { firstName, lastName } =
user; return${firstName} ${lastName}
; }// best
function getFullName({ firstName, lastName }) { return
${firstName} ${lastName}
; }
- 用数组结构
const arr = [1, 2, 3, 4];
// bad
const first = arr[0]; const second = arr[1];// good
const [first, second] = arr;
Strings
- 对string 用单引号
- 超过100个字符的字符串不应该用string串联成多行
Why? 被折断的字符串工作起来是糟糕的而且使得代码更不易被搜索。
- 用字符串模板而不是字符串拼接来组织可编程字符串
// bad
function sayHi(name) { return 'How are you, ’ + name + ‘?’; }// bad
function sayHi(name) { return [‘How are you, ‘, name,’?’].join(); }// good
function sayHi(name) { returnHow are you, ${name}?
; }
- 永远不要在字符串中用eval()。
- 不要使用不必要的转义字符。
Functions
- 用命名函数表达式而不是函数声明。
函数表达式:const func = function(){}
函数声明:function func(){}
// bad
function foo() { // … }// bad
const foo = function () { // … };// good
// lexical name distinguished from the variable-referenced invocation(s)
// 函数表达式名和声明的函数名是不一样的
const short = function
longUniqueMoreDescriptiveLexicalFoo() { // … };
- 不要使用arguments,用rest语法…代替。
// bad
function concatenateAll() { const args =
Array.prototype.slice.call(arguments); return args.join(’’); }// good
function concatenateAll(…args) { return args.join(’’); }
- 把默认参数赋值放在最后
// bad
function handleThings(opts = {}, name) { // … }// good
function handleThings(name, opts = {}) { // … }
- 不要对参数重新赋值
// bad
function f1(a) { a = 1; // … }function f2(a) { if (!a) { a = 1; } // … }
// good
function f3(a) { const b = a || 1; // … }function f4(a = 1) { // … }
Arrow Functions
- 当你一定要用函数表达式(在回调函数里)的时候就用箭头表达式吧
Why? 他创建了一个this的当前执行上下文的函数的版本,这通常就是你想要的;而且箭头函数是更简洁的语法
Why? 什么时候不用箭头函数:如果你有一个相当复杂的函数,你可能会把这个逻辑移出到他自己的函数声明里。
// bad
[1, 2, 3].map(function (x) { const y = x + 1; return x * y;
});// good
[1, 2, 3].map((x) => { const y = x + 1; return x * y; });
Classes & Constructors
- 常用
class
,避免直接操作prototype
// 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; } }
- 用extend实现继承
Why? 它是一种内置的方法来继承原型功能而不打破instanceof
// 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]; } }
Modules
- 用
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;
- 不要用import 通配符,就是*这种方式
// bad
import * as AirbnbStyleGuide from ‘./AirbnbStyleGuide’;// good
import AirbnbStyleGuide from ‘./AirbnbStyleGuide’;
- 不要导出可变的东西
Why? 变化通常都是需要避免,特别是当你要输出可变的绑定。虽然在某些场景下可能需要这种技术,但总的来说应该导出常量。
// bad
let foo = 3; export { foo }// good
const foo = 3; export { foo }
- 在一个单一导出模块里,用export default 更好
// bad
export function foo() {}// good
export default function foo() {}
Iterators and Generators
- 不要用遍历器。用 JavaScript 高级函数代替for-in / for -of 。
Why? 用数组的这些迭代方法:
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; } 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;// 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
(keeping it functional) const increasedByOne = numbers.map(num
=> num + 1);
- generator 不建议用,因为在es5上支持不好
Variables
- const / let 就不多说了
- 不要使用一元自增自减运算符(++,–)
Why? 根据eslint文档,一元增量和减量语句受到自动分号插入的影响,并且可能会导致应用程序中的值递增或递减的无声错误。 使用num +> = 1而不是num ++语句来表达你的值也是更有表现力的。 禁止一元增量和减量语句还会阻止您无意地预增/预减值,这也会导致程序出现意外行为。
Comparison Operators & Equality
- 用 === 和 !== 而不是 == 和 !=
- 布尔值用缩写,数字和字符串要明确比较对象
// bad
if (isValid === true) { // … }// good
if (isValid) { // … }// bad
if (name) { // … }// good
if (name !== ‘’) { // … }// bad
if (collection.length) { // … }// good
if (collection.length > 0) { // … }
- 三元表达式不应该嵌套,通常是单行表达式
- 如果if 语句中总是需要用return,那后续的else就不需要写了。if块中包含return,它后面的else if 块中也包含了return ,这个时候就可以把return 分到多个 if 语句中。
// bad
function foo() { if (x) {
return x; } else {
return y; } }// bad
function cats() { if (x) {
return x; } else if (y) {
return y; } }// bad
function dogs() { if (x) {
return x; } else {
if (y) {
return y;
} } }// good
function foo() { if (x) {
return x; }return y; }
// good
function cats() { if (x) {
return x; }if (y) {
return y; } }// good
function dogs(x) { if (x) {
if (z) {
return y;
} } else {
return z; } }
Comments
- 在你的注释前使用FIXME 或TODO前缀,这有助于其他开发人员快速理解你指出的需要重新访问的问题,或者建议需要实现的问题的解决方案,这些不同于常规注释,他们是可操作的。动作是FIXME:需要计算出来 TODO :需要实现
- // FIXME : 给问题做注释
class Calculator extends Abacus { constructor() {
super();// FIXME: shouldn't use a global here total = 0; } }
- 用// TODO: 去注释问题的解决方案
class Calculator extends Abacus { constructor() {
super();// TODO: total should be configurable by an options param this.total = 0; } }
Naming Conventions
- 用小驼峰式命名对象/函数/实例
- 用大驼峰式命名类
- 不要用前置或后置下划线
- 不要保存使用this,用箭头函数或函数绑定
// bad
function foo() { const self = this; return function () {
console.log(self); }; }// bad
function foo() { const that = this; return function () {
console.log(that); }; }// good
function foo() { return () => {
console.log(this); }; }
- export default 导出模块A,则这个文件名也叫A.*,import 时候的参数也叫A,大小写一致。
- 当export default一个函数时,函数名用小驼峰,文件名需要和函数名一致
- 当export一个结构体/类/单例/函数库/对象 时用大驼峰
- 大写字母设置静态变量,需要满足三个条件
a . 导出变量
b . 是const定义的
c . 变量可信且他的子属性都是不能改变的