- 2 空格缩进
- UTF-8 编码
// bad
function() {
const name
}
// bad
function() {
const name
}
// good
function() {
const name
}
Google、Twitter、Facebook、Github、Mozilla 以及 npm 上 top 10 下载中有 7 个是 2 个空格的
// bad
if (condition)
{
// ...
}
// good
if (condition) {
// ...
}
// 链式调用
target.setPosition(300, 50)
.moveTo(700, 500)
// 超长的三元运算
const result = thisIsAVeryVeryLongCondition
? resultA : resultB
function setStyle(element, property, value) {
if (element == null) return
element.style[property] = value
}
// bad
const obj = {
foo() {
},
bar() {
}
}
// good
const obj = {
foo() {
},
bar() {
}
}
// bad
const x=y+5
// good
const x = y + 5
// bad
const isValid = !! valid
// good
const isValid = !!valid
// bad
if(condition){
// ...
}
// good
if (condition) {
// ...
}
// bad
while(condition){
// ...
}
// good
while (condition) {
// ...
}
// bad
(function () {
})()
// good
(function() {
})()
// bad
const obj = {
a:1,
b:2,
c:3
}
// good
const obj = {
a: 1,
b: 2,
c: 3
}
// bad
import {Person, Relation} from 'zone'
export {name, age}
// good
import { Person, Relation } from 'zone'
export { name, age }
// bad
callFunc( param1, param2, param3 )
save( this.list[ this.indexes[ i ] ] )
const arr = [ 1, 2, 3 ]
const obj = { name: 'obj' }
needIncreament && ( variable += increament )
if ( num > list.length ) {
}
while ( len-- ) {
}
// good
callFunc(param1, param2, param3)
save(this.list[this.indexes[i]])
const arr = [1, 2, 3]
const obj = {name: 'obj'}
needIncream && (variable += increament)
if (num > list.length) {
}
while (len--) {
}
// bad
const hangModules = [],
missModules = [],
visited = {}
// good
const hangModules = []
const missModules = []
const visited = {}
// bad
let i
const items = getItems()
let dragonball
const goSportsTeam = true
let len
// good
const goSportsTeam = true
const items = getItems()
let dragonball
let i
let length
// bad
if (condition)
callFunc()
// good
if (condition) callFunc()
// good
if (condition) {
callFunc()
}
// bad
function() { return false }
// good
function() {
return false
}
// bad
if (condition) {
thing1()
thing2()
}
else {
thing3()
}
// good
if (condition) {
thing1()
thing2()
} else {
thing3()
}
// bad
const task = function () {
// ...
}()
// bad
const task = (function () {
// ...
}())
// good
const task = (function () {
// ...
})()
不需要,ES6 编译成 ES5 后会自动添加
使用单引号
// bad
const name = "Capt. Janeway"
// good
const name = 'Capt. Janeway'
常量全部大写的方式阅读起来比较困难
// bad
function insertHtml(element, html) {
// ...
}
// good
function insertHTML(element, html) {
// ...
}
// bad
this.__firstName__ = 'Panda'
this.firstName_ = 'Panda'
// good
this._firstName = 'Panda'
JavaScript 没有私有属性的概念,虽然这样做并不能防止有人滥用,但至少可以阐明意图,这样做是错误的。
严禁滥用下划线,比如局部变量的声明。作用域本身限制了局部变量不可被外部访问。
// bad
function doSomething() {
let _name = 'Panda'
let _age = 11
}
// bad
function q() {
// ...
}
// good
function query() {
// ...
}
// 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)
}
}
// bad
dragon.age()
// good
dragon.getAge()
// bad
dragon.age(25)
// good
dragon.setAge(25)
const isReady = false
const hasMoreCommands = function() {
// ...
// return Boolean
}
使用 /** ... */
作为多行注释,参考JSDoc
// good
/**
* make() returns a new element
* based on the passed in tag name
*
* @param {String} tag
* @return {Element} element
*/
function make(tag) {
// ...
return element
}
// bad
const active = true // is current tab
// good
// is current tab
const active = true
// bad
function getType() {
console.log('fetching type...')
// set the default type to 'no type'
const type = this._type || 'no type'
return type
}
// good
function getType() {
console.log('fetching type...')
// set the default type to 'no type'
const type = this._type || 'no type'
return type
}
方便自己和他人明确代码存在的问题
class Calculator {
constructor() {
// TODO: total should be configurable by an options param
this.total = 0
}
}
let
声明的变量属于块级作用域
let
不会带来变量提升(Hoisting)
// bad
for (var i = 0; i < 3; i++) {
// ...
}
console.log(i) // 3
// good
for (let i = 0; i < 3; i++) {
// ...
}
console.log(i) // i is not defined
如果变量不需要重新分配值,全部用 const
声明
const
声明后,无法修改引用的值,可避免被重写
const
也属于块级作用域
const
不会带来变量提升(Hoisting)
// bad
const x = obj.x
const y = obj.y
const a = arr[0]
const b = arr[1]
// good
const { x, y } = obj
const [a, b] = arr
const reviewScore = 9
// bad
const totalScore = reviewScore + ''
// 实际调用: reviewScore.valueOf()
const obj = {
valueOf: () => 1
}
obj + '' // '1' 而不是 '[object Object]'
// bad
const totalScore = reviewScore.toString() // toString 方法可能被改写
// good
const totalScore = String(reviewScore)
const inputValue = '4'
// bad
const val = +inputValue
// bad
const val = new Number(inputValue)
// bad
/**
* JavaScript 位运算完全套用 Java,有三个问题:
* 1、位操作针对的是整数,小数部分会被舍弃
* 2、整数不能超过32位
* 3、JavaScript 的数字都是以双精度浮点数存储的,实际执行还需要先转换为整数
*/
const val = inputValue >> 0
// good
const val = Number(inputValue)
使用
parseInt
时,如果不指定进制,转换的变量以0
开头(比如一些月份和天),ECMAScript 3 会当作 8 进制。
const inputValue = '200px'
// bad
const val = parseInt(inputValue)
// good
const val = parseInt(inputValue, 10)
const age = 0
// bad
const hasAge = new Boolean(age)
// good
const hasAge = Boolean(age)
// good
const hasAge = !!age
严格判断会检查对象的类型,避免隐式的类型转换
0 == false // true
0 == '0' // true
0 === false // false
0 === '0' // false
// bad
if (collection.length > 0) {
// ...
}
// good
if (collection.length) {
// ...
}
switch
的方式需要逐条 case
判断且匹配的 case
,如果漏掉 break
,会执行下一条 case
(不论是否满足)或 default,直到遇到 break
为止。
使用字典对象代替,速度更快,同时避免未预料的结果。
const cases = {
alpha: function() {
// ...
},
beta: function() {
// ...
},
_default: function() {
// ...
}
}
// bad
for (let i = 0, len = elements.length; i < len; i++) {
const element = elements[i]
addListener(element, 'click', function () {})
}
// good
function clicker() {
// ...
}
for (let i = 0, len = elements.length; i < len; i++) {
const element = elements[i]
addListener(element, 'click', clicker)
}
虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次
length
访问时会动态计算元素个数,此时缓存length
能有效提高程序性能。
// bad
for (let i = 0; i < arr.length; i++) {
console.log(i)
}
// good
for (let i = 0, len = arr.length; i < len; i++) {
console.log(i)
}
for (let i = elements.length; i--; ) {
const element = elements[i]
}
无需额外变量缓存集合长度
前测条件只需判断数字是否为 true,无需大小比较
无需运行后执行体
避免了数组越界
for (let keys = Object.keys(obj), i = keys.length; i--; ) {
const key = keys[i]
// ...
}
for in
的速度很慢
无需遍历原型链的可枚举属性
无需
hasOwnProperty
判断来避免遍历原型链(Object.create(null)
除外 )
10万个属性的对象两种遍历方式在 Chrome48 测试结果:
for in
: 143ms
Object.keys
+ for
: 45ms
const name = 'lucy'
// bad
const greetings = 'Hello ' + name
// good
const greetings = `Hello ${name}`
const html = `
<article>
<h1>Title here</h1>
<p>This is a paragraph</p>
<footer>Complete</footer>
</article>
`
// bad
const items = new Array()
// good
const items = []
// bad
const list = [1, 2, 3]
const result = list.concat()
// good
const list = [1, 2, 3]
const result = [...list]
const foo = document.querySelectorAll('.foo')
// bad
const nodes = Array.prototype.slice.call(foo, 0)
// good
const nodes = Array.from(foo)
// bad
const item = new Object()
// good
const item = {}
// bad
const superman = {
default: { clark: 'kent' },
'bar': true,
'data-blah': 5
}
// good
const superman = {
defaults: { clark: 'kent' },
bar: true,
'data-blah': 5
}
// bad
JSON.stringify = function() {
// ...
}
如果必须重写内置方法,功能上保持一致性
所有属性可在对象创建的时候一次性定义
// bad
const prop = condition ? 'testA' : 'testB'
const item = {
test: 0
}
item[prop] = true
// good
const prop = condition ? 'testA' : 'testB'
const item = {
test: 0,
[prop]: 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
}
使用
...
能表明你要传入的参数
rest 参数是一个真正的数组,而
arguments
是一个类数组
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments)
return args.join('')
}
// good
function concatenateAll(...args) {
return args.join('')
}
默认值只会在参数未传入或值为
undefined
的情况下被使用
// bad
function handleThings(opts) {
opts = opts || {}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
箭头函数体内的
this
对象为定义时所在的对象而不是使用时所在的对象且写法更简洁
// bad
[1, 2, 3].map(function (x) {
this.count += x
return x * x
}, this)
// good
[1, 2, 3].map(x => {
this.count += x
return x * x
})
// bad
[1, 2, 3].map((x) => {
return x * x
})
// good
[1, 2, 3].map(x => x * x)
class
语法更符合标准面向对象结构,更简洁易读
// bad
function Queue(contents = []) {
this._queue = [...contents]
}
Queue.prototype.pop = function() {
return this._queue.pop()
}
// good
class Queue {
constructor(contents = []) {
this._queue = [...contents]
}
pop() {
return this._queue.pop()
}
}
// 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]
}
}
// bad
const styleGuide = require('./styleGuide')
// good
import styleGuide from './styleGuide'
动态代码如果通过其他来源传入
eval('document.' + potato + '.style.color = "red"')
,可能导致注入攻击
动态代码调试起来不方便,如具体的行数不明
动态代码执行更慢(不能编译、缓存)
eval
直接调用时,作用域为当前作用域,间接调用时,作用域为全局作用域,可能会造成干扰
// 直接调用
function test() {
let count = 0
eval('count++')
}
// 相当于
function test() {
let count = 0
count++
}
// 间接调用
function test() {
let count = 0
const myEval = eval
myEval('count++')
}
// 相当于
function test() {
let count = 0
}
count++
new Function
相当于在全局作用域下声明一个函数,不会干扰其他作用域
function test() {
const foo = new Function('name', 'return name')
}
// 相当于
function test() {
}
const foo = function(name) {
return name
}
动态代码的执行作用域是全局的,会影响全局作用域
// bad
setTimeout('a++', 0)
// 与HTML上直接定义事件相同
element.setAttribute('onclick', 'doSomething()')
// good
setTimeout(function() {
a++
}, 0)
element.addEventListener('click', function() { ... }, false)
with
作用域下如果没找到,会向父级寻找,可能造成未预料的结果
// 如果 o 没有属性 x,则会读取参数 x,可能不是想要的结果
function f(x, o) {
with (o)
print(x)
}
使用变量缓存 DOM 对象
// bad
document.getElementById('container').setAttribute('class', 'active')
document.getElementById('container').setAttribute('index', 0)
// good
const el = document.getElementById('container')
el.setAttribute('class', 'active')
el.setAttribute('index', 0)
页面 reflow 是非常耗时的行为,非常容易导致性能瓶颈。下面一些场景会触发浏览器的reflow:
- DOM 元素的添加、修改(内容)、删除
- 应用新的样式或者修改任何影响元素布局的属性
- Resize 浏览器窗口、滚动页面
- 读取元素的某些属性,如
offsetLeft
、offsetTop
、offsetHeight
、offsetWidth
、scrollTop/Left/Width/Height
、clientTop/Left/Width/Height
、getComputedStyle()
、currentStyle
(IE)
// bad
el.style.width = '100px'
el.style.height = '100px'
while (i--) {
el.style.left = el.offsetWidth + 10 + 'px'
}
// good
el.style.cssText = 'width: 100px; height: 100px;'
const offsetWidth = el.offsetWidth
while (i--) {
el.style.left = offsetWidth + 10 + 'px'
}
操作 document fragment 是在内存中操作而非 DOM 树下,不会导致 reflow
// bad
for (let i = 0; i < 5; i++) {
const li = document.createElement('li')
ul.appendChild(li)
}
// good
const docFrag = document.createDocumentFragment()
for (let i = 0; i < 5; i++) {
const li = document.createElement('li')
docFrag.appendChild(li)
}
ul.appendChild(docFrag)
childNodes
的范围包括children
、文本、注释和属性节点
addEventListener / attachEvent
可绑定多个事件
直接在 HTML 属性中或 DOM 的属性绑定事件属于动态代码,在全局作用域下执行
代码规范检查