类型
- 在使用复合类型时需要注意其引用:
const foo = [1, 2];
const bar = foo;
bar[0] = 2;
console.log(foo, bar); // => [2, 2], [2, 2]
变量声明与赋值
- 使用
let
,const
代替var
:
let 和 const 是块级作用域(block-scoped), 而 var 是函数作用域(function-scoped), 使用let/const可以有效避免变量污染作用域
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
- 避免使用连续赋值
// bad
var a = 1, b, c;
// good
let a = 1;
let b;
let c;
- 不要使用链式赋值操作
可能污染全局变量
// bad
(function example() {
// JavaScript interprets this as
// let a = ( b = ( c = 1 ) );
// The let keyword only applies to variable a; variables b and c become
// global variables.
let a = b = c = 1;
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// the same applies for `const`
- 在合适的位置分配变量
// bad - 当 'hasName' === 'test' 时, `const name = getName`不必要
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
// good
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
- 避免使用自增/自减运算符
可能受到行尾自动插入分行的影响
- 变量声明就一定要使用
对象 & 数组
- 避免使用new 操作符创建对象/数组
// bad
const item = new Object();
// good
const item = {};
- 使用对象的计算属性来完成动态对象属性
在一个地方即可定义对象定义, 使代码更可读
function getKey(k) {
return `a key named ${k}`;
}
// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
- 使用对象属性/方法的缩写
// 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,
};
- 将对象属性缩写与不缩写的属性分组区别开
可以更直观得看到缩写的属性
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
- 对象的属性只有在属性名不合法时使用引号, 且使用单引号
更易读, 并且更容易被js引擎优化
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
- 不要直接调用对象原型上面的方法, 如:
hasOwnProperty
,propertyIsEnumerable
,isPrototypeOf
:
避免调用冲突, { hasOwnProperty: false }, 并且调用对象可以是null (Object.create(null))
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
// ...
console.log(has.call(object, key));
- 使用
...
操作符浅拷贝对象/数组
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // 污染了原对象
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
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
- 使用
Array.from
来将一个类数组的对象, 转化为真数组
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
解构
- 使用对象解构来操作多属性的对象 / 数组
可以避免声明许多临时变量
// 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;
字符串
- 字符串不换行
不易读并其不利于搜索
- 使用字符串模板
更可读, 更少的冗余代码
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// bad
function sayHi(name) {
return `How are you, ${ name }?`;
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
- 避免使用不必要的转义字符
使代码更不易理解
// bad
const foo = '\'this\' \i\s \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
- 使用命名函数表达式代替函数声明, 并且声明函数时指定函数名称
函数声明被提升, 这意味着在函数定义之前使用函数变得很容易, 会损害代码的可读性和可维护性, 指定函数名称可以方便debug
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// 与变量引用的调用区分开的函数名称
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
- 使用括号包含立即执行函数
括号表示该立即执行函数是一个独立的单元
// bad
function () {
console.log('Welcome to the Internet. Please follow me.');
}();
// good
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
- 注意函数声明会被提升
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
- 函数的形参中不要声明
arguments
, 可使用...
操作符
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
// good
function foo (name, age, ...options){
// ...
}
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 尽量使用默认参数
// really bad
function handleThings(opts) {
// 不应该直接改变 函数参数
// 如果 opts 为逻辑假, 那么它将被赋值为一个空对象
// be what you want but it can introduce subtle bugs.
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
- 在使用默认参数时注意其副作用
可能导致相同的调用不同的结果
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
- 避免使用
Function
构造函数(new Function)
跟 eval 一样, 可能引起严重漏洞
- 优先使用箭头函数
更简洁, 且箭头函数没有烦人的this问题
// 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;
});
类和构造函数
- 使用
class
关键字创建一个类
更简单, 更可读
- 使用
extends
来继承类
这是一种内建的不破坏
instanceof
的方法
- 类方法可以
return this
来方便连式调用
// bad
Jedi.prototype.jump = function () {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function (height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
- 定义类的toString方法, 方便某些场景下使用(alert)
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
const qwang = new Jedi({name: 'qwang'})
alert(qwang) // 'Jedi - qwang'
- 在没有显示声明
constructor
时, class 默认会有一个constructor
方法.
// bad
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}
// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
迭代
- 尽可能使用js内建的高阶函数来代替
for ...in
和for ...of
贯彻不可变原则, 代码处理函数返回值比副作用更可推导
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);
- 摘选自eslint