[原文链接]http://feclub.cn/post/content/eslint
1. ESLint 规范选讲
1.1 References
1.1.1 优先使用 const 来声明变量; 避免使用 var.
- eslint: prefer-const, no-const-assign
1.1.2 如果申明的变量必须要改变, 使用 let.
- eslint: no-var
针对不需要改变的变量,优先使用 const,这样可以保证不会在其它地方意外地改变这个值或者引用。
// bad
var a = 1
var b = {}
// 👍
const a = 1
const b = {}
// bad
var count = 1
if (true) {
count += 1
}
// good, use the let.
let count = 1
if (true) {
count += 1
}
1.2 Objects
1.2.1 Use property value shorthand.
- eslint: object-shorthand
const lukeSkywalker = 'Luke Skywalker'
// bad
const obj = {
lukeSkywalker: lukeSkywalker
}
// good
const obj = {
lukeSkywalker
}
1.2.2 浅拷贝时使用拓展运算符,而不是 Object.assign
// 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 }
拓展运算符在类数组转换和数组去重中的运用
1.3 Arrays
1.3.1 使用拓展运算符复制数组。
// bad
const len = items.length
const itemsCopy = []
let i
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i]
}
// good
const itemsCopy = [...items]
[...new Set(arr)]
1.3.2 使用...代替 Array.from 来进行类数组至数组的转换。
const foo = document.querySelectorAll('.foo')
arguments
// good
const nodes = Array.from(foo)
// best
const nodes = [...foo]
1.4 Destructuring
1.4.1 对象的解构赋值
- eslint: prefer-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}`
}
1.4.2 数组的解构赋值
- eslint: prefer-destructuring
const arr = [1, 2, 3, 4]
// bad
const first = arr[0]
const second = arr[1]
// good
const [first, second, ...rest] = arr
1.4.3 优先使用模版字符串
// 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}?`
}
1.5 Functions
1.5.1 合理使用函数参数默认值
// really bad
function handleThings(opts) {
opts = opts || {}
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {}
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
1.5.2 不要直接改变函数参数. eslint: no-param-reassign
// bad
function f1(obj) {
obj.key = 1
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1
}
// 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) {
// ...
}
1.5 Classes
1.5.1 使用 Class 和 extends
- 简洁易于理解,语义性强
// 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]
}
static pop() {
const value = this.queue[0]
this.queue.splice(0, 1)
return value
}
}
// 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]
}
}
1.6.2 使用方法执行链
// 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)
1.final
- 合理使用编辑器的 eslint fix 功能
- 格式化不要使用自己的个性化配置,按照项目内的 eslint 配置 进行格式化
2. 代码重构
2.1 提炼函数
- 避免超大函数
- 利于代码复用
- 利于单元测试
const getInfo = function(params) {
ajax('http://baidu.com/api', data => {
global.name = data.name
global.age = data.age
global.sex = data.sex
})
}
// 抽离
const getInfo = function(params) {
ajax('http://baidu.com/api', data => {
setInfo(data)
})
}
const setInfo = function(data) {
global.name = data.name
global.age = data.age
global.sex = data.sex
}
2.2 合并重复条件
- 例如函数体内有一些分支条件,每个分支条件内散步了一些重复的代码
const paging = function(curPage) {
if (curPage <= 0) {
curPage = 0
} else if (curPage >= totalPage) {
curPage = totalPage
}
jump(curPage)
}
2.3 条件抽离
- 复杂的判断条件应该抽离成单独的函数
const getPrice = function(goods) {
if (goods.price > 10000 && goods.price < 20000 && goods.isCheap) {
return goods.price * 0.8
}
return goods.price
}
const isNeedDiscount = function(goods) {
return goods.price > 10000 && goods.price < 20000 && goods.isCheap
}
const getPrice = function(goods) {
if (isNeedDiscount(goods)) {
return goods.price * 0.8
}
return goods.price
}
2.4 熟悉并运用各种数组和对象的方法
避免:
- 遍历数组只会 forEach 或者 for(){}
给数组添加元素只会用 push。
push()
push()
应该:考虑 every、some、filter、map、reduce 等等
- 考虑 concat、unshift、splice 等等
2.5 事不过三原则
- 重复或者类似的条件和功能出现达到三次,这段代码就是很垃圾的代码。
- 经常出现在诸如判断接口 code,判断用户各种状态的场景中
const cal = function(lev, money) {
if (lev === 'A') {
return money * 10
}
if (lev === 'B') {
return money * 8
}
if (lev === 'C') {
return money * 6
}
if (lev === 'D') {
return money * 0
}
}
const calFuncMap = {
A: money => money * 10,
B: money => money * 8,
C: money => money * 6,
D: money => money * 0
}
calFuncMap['A'](5000)
const calMap = {
A: 10,
B: 8,
C: 6,
D: 0
}
const cal = function(lev, money) {
return money * calMap[lev]
}
2.5 提前退出,代替 if 嵌套
if (a) {
foo()
if (b) {
bar()
if (c) {
boo()
}
}
}
// good
if (!a) return
foo()
if (!b) return
bar()
if (!c) return
boo()
2.6 主动应用上述 ESLint 建议的内容
- 链式调用
- 解构复制
- 默认参数
- Class
- ...