- 不使用分号
- 使用分号
为什么? 当 JavaScript 遇到没有分号的换行符时,它使用一组称为自动分号插入的规则来确定是否应该将换行符视为语句的结尾,并且(顾名思义)如果被这样认为的话,在换行符前面自动插入一个分号。ASI(自动分号插入)包含了一些稀奇古怪的的行为,不过,如果 JavaScript 错误地解释了你的换行符,你的代码将会被中断执行。随着新功能成为 JavaScript 的一部分,这些规则将变得更加复杂。明确地结束你的语句并配置你的 linter 来捕获缺少的分号,将有助于防止遇到问题
不使用逗号结尾
使用逗号结尾
使用let,const,摒弃var
const a = [1, 2], const b = a, b[0] = 9, a = ?, b = ?
- 使用字面量创建Object和Array
// bad
const obj = new Object(), const arr = new Array()
// good
const obj = {1, 2, 3}, const arr = [7, 8, 9]
- {}前后要空格
{ 1, 2, 3 }
- []前后无需空格
[1, 2, 3]
- 注释符号后面要空格
// 注释
- 逗号之后要空格
const a = [1, 2, 3]
- 冒号之后要空格
const obj = { abc: 123 }
- 使用对象方法速记语法
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
}
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
}
- 使用表达式创建funtion
// 为什么? 函数声明很容易被提升(Hoisting),你可以在函数被定义之前引用该函数。这对可读性和可维护性来说都是不利的。
// bad
function fn () {}
// good
const fn = () => {}
- 使用对象属性速记语法
// bad
const obj = { abc: abc }
// good
const obj = { abc }
- 只用引号引无效标识符的属性
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
}
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
}
- 使用展开符…复制数组
// 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];
- 在数组方法回调中使用 return 语句。如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值
// bad
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
} else {
return false
}
})
// good
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
}
return false
})
- 当访问和使用对象的多个属性时,请使用对象解构
// 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
const name = "Capt. Janeway"
// bad - 模板字面量应该包含插值或换行符
const name = `Capt. Janeway`
// good
const name = 'Capt. Janeway'
- 以编程方式构建字符串时,请使用模板字符串而不是字符串连接
// 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}?`
}
- 不要使用 arguments,可以选择 rest 语法 ... 替代
//为什么?使用 ... 能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而 arguments 是一个类数组(Array-like)。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 使用默认参数语法,而不要使用一个变化的函数参数
// bad
function handleThings(opts) {
opts = opts || {};
// 如果opts的值就是false怎么办?按上面的写法,它永远等于{}了,也就是true
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
- 不要改变参数
// 为什么?因为对象是引用类型,操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。
// 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) {
// ...
}
- 如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号
// bad
[1, 2, 3].map((x) => x * x)
// good
[1, 2, 3].map(x => x * x)
// good
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
))
// bad
[1, 2, 3].map(x => {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map((x) => {
const y = x + 1
return x * y
})
- 一个地方只在一个路径中 import(导入)
// bad
import foo from 'foo'
// … 其他一些 imports … //
import { named1, named2 } from 'foo'
// good
import foo, { named1, named2 } from 'foo'
// good
import foo, {
named1,
named2,
} from 'foo'
- 将所有 import 导入放在非导入语句的上面
// bad
import foo from 'foo'
foo.init()
import bar from 'bar'
// good
import foo from 'foo'
import bar from 'bar'
foo.init()
- 不要使用 iterators(迭代器) 。请使用高阶函数,例如 map() 和 reduce() 等�,而不是像 for-in 或 for-of 这样的循环
// 为什么? 这是强制执行我们不变性的规则。 处理返回值的纯函数比副作用更容易推理。
// 使用 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
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
const increasedByOne = numbers.map(num => num + 1)
- 使用 const 或 let声明每个变量
const items = getItems()
const goSportsTeam = true
const dragonball = 'z'
- 避免使用一元递增和递减运算符(++, --)
let a = 1
// bad
a++
// good
a += 1
尽量使用 === 和 !== 而非 == 和 !=
在 case 和 default 子句中,使用大括号来创建包含词法声明的语句块
// bad
switch (foo) {
case 1:
let x = 1;
break
case 2:
const y = 2;
break
case 3:
function f() {
// ...
}
break
default:
class C {}
}
// good
switch (foo) {
case 1: {
let x = 1;
break
}
case 2: {
const y = 2;
break
}
case 3: {
function f() {
// ...
}
break
}
case 4:
bar()
break
default: {
class C {}
}
}
- 三元表达式不应该嵌套,通常写成单行表达式
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null
// 拆分成2个分离的三元表达式
const maybeNull = value1 > value2 ? 'baz' : null
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull
- 避免不必要的三元表达式语句
// 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
// bad
const bar = a ** b - 5 % d
// bad
if (a || b && c) {
return d
}
// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0)
// good
const bar = (a ** b) - (5 % d)
// good
if ((a || b) && c) {
return d
}
// good
const bar = a + b / c * d
- 如果一个 if 块总是执行一个 return 语句,后面的 else 块是不必要的。在 else if 块中的 return,可以分成多个 if 块来 return
// 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
}
}
- 在大括号前放置 1 个空格
// bad
function test(){
console.log('test')
}
// good
function test() {
console.log('test')
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
})
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
})
- 在控制语句(if、while 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格
// bad
if(isJedi) {
fight ()
}
// good
if (isJedi) {
fight()
}
// bad
function fight () {
console.log ('Swooosh!')
}
// good
function fight() {
console.log('Swooosh!')
}
- 使用空格把运算符隔开
// bad
const x=y+5
// good
const x = y + 5
在文件末尾插入一个空行
在声明语句的开始处就执行强制类型转换
创建字符串
// => this.reviewScore = 9
// bad
const totalScore = new String(this.reviewScore) // typeof totalScore 是 "object" 而不是 "string"
// bad
const totalScore = this.reviewScore + '' // 调用 this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString() // 不能保证返回一个字符串
// good
const totalScore = String(this.reviewScore)
- 数字: 使用 Number 进行转换,而 parseInt 则始终以基数解析字串
const inputValue = '4'
// bad
const val = new Number(inputValue)
// bad
const val = +inputValue
// bad
const val = inputValue >> 0
// bad
const val = parseInt(inputValue)
// good
const val = Number(inputValue)
// good
const val = parseInt(inputValue, 10)
- 创建布尔值
const age = 0
// bad
const hasAge = new Boolean(age)
// good
const hasAge = Boolean(age)
// best
const hasAge = !!age
避免使用单字母名称。使你的命名具有描述性
当命名对象,函数和实例时使用驼峰式命名
当命名构造函数或类的时候使用单词首字母大写方式命名
将绑定数据到事件时 (不论是 DOM 事件还是其他像Backbone一类的事件), 传递 hash 而不是原始值。 这将允许后续的贡献者不用查找和更新事件的每一个处理程序就可以给事件添加更多的数据
// bad
$(this).trigger('listingUpdated', listing.id)
// ...
$(this).on('listingUpdated', (e, listingId) => {
// do something with listingId
})
// good
$(this).trigger('listingUpdated', { listingId: listing.id })
// ...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingId
})
- Number.isNaN 代替全局 isNaN
// 为什么?全局的 isNaN 方法会将非数字转换为数字, 任何被转换为 NaN 的东西都会返回 true
// bad
isNaN('1.2') // false
isNaN('1.2.3') // true
// good
Number.isNaN('1.2.3') // false
Number.isNaN(Number('1.2.3')) // true
- Number.isFinite 代替全局 isFinite
// 为什么?全局的 isFinite 方法会将非数字转换为数字, 任何被转换为有限大的数字都会返回 true
// bad
isFinite('2e3') // true
// good
Number.isFinite('2e3') // false
Number.isFinite(parseInt('2e3', 10)) // true