本规范用于指导前端开发代码规范参考

# 1 常量

# 1.1 使用 const 您的所有引用的 避免使用 var。

为什么?这样可以确保您不能重新分配引用,这可能导致错误和难以理解的代码。

// bad
var a = 1
var b = 2
// good
const a = 1
const b = 2

# 1.2 如果必须重新分配引用,请使用 let 代替 var。

为什么?let 是像块一样,而不是像函数一样 var。

// bad
var count = 1
if (true) {
  count += 1
}
// good, use the let.
let count = 1
if (true) {
  count += 1
}

# 1.3 请注意,let 和 const 都是块范围的。

// const and let only exist in the blocks they are defined in.
{
  let a = 1
  const b = 1
}
console.log(a) // ReferenceError
console.log(b) // ReferenceError

# 2 对象 Objects

# 2.1 使用文字语法进行对象创建。

// bad
const item = new Object()
// good
const item = {}

# 2.2 创建具有动态属性名称的对象时,请使用计算的属性名称。

为什么?它们使您可以在一处定义对象的所有属性。

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,
}

# 2.3 使用对象方法的简写。

// bad
const atom = {
  value: 1,
  addValue: function (value) {
    return atom.value + value
  },
}
// good
const atom = {
  value: 1,
  addValue(value) {
    return atom.value + value
  },
}

# 2.4 使用属性值速记

为什么?它简短而具有描述性。

const lukeSkywalker = 'Luke Skywalker'
// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
}
// good
const obj = {
  lukeSkywalker,
}

# 2.5 在对象声明的开头对速记属性进行分组

为什么?比较容易分辨出哪些属性正在使用速记。

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,
}

2.6 仅引用无效标识符的属性

为什么?总的来说,我们认为它在主观上更容易阅读。它改善了语法突出显示的功能,并且也容易被许多 JS 引擎优化。

// bad
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5,
}
// good
const good = {
  foo: 3,
  bar: 4,
  'data-blah': 5,
}

# 2.7 不叫 Object.prototype 直接的方法,如 hasOwnProperty,propertyIsEnumerable 和 isPrototypeOf。

为什么?这些方法可能会受到所讨论对象的属性的影响 - 考虑 {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 // cache the lookup once, in module scope.
console.log(has.call(object, key))
/* or */
import has from 'has' // https://www.npmjs.com/package/has
console.log(has(object, key))

# 2.8 首选对象散布运算符,而不是 Object.assign 浅表复制对象。使用对象剩余运算符可获取省略了某些属性的新对象。

// very bad
const original = { a: 1, b: 2 }
const copy = Object.assign(original, { c: 3 }) // this mutates `original` ಠ_ಠ
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 }

# 3 数组 Array

# 3.1 使用文字语法创建数组。

// bad
const items = new Array()
// good
const items = []

# 3.2 使用 Array#push 而不是直接分配将项目添加到数组。

const someStack = []
// bad
someStack[someStack.length] = 'abracadabra'
// good
someStack.push('abracadabra')

# 3.3 使用数组跨度... 复制数组。

// bad
const len = items.length
const itemsCopy = []
let i
for (i = 0 i < len i += 1) {
  itemsCopy[i] = items[i]
}
// good
const itemsCopy = [...items]

# 3.4 要将可迭代对象转换为数组,请使用扩展... 而不是 Array.from。

const foo = document.querySelectorAll('.foo')
// good
const nodes = Array.from(foo)
// best
const nodes = [...foo]

# 3.5 使用 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)

# 3.6 使用 Array.from 而不是扩展... 来映射可迭代对象,因为它避免了创建中间数组。

// bad
const baz = [...foo].map(bar)
// good
const baz = Array.from(foo, bar)

# 3.7 在数组方法回调中使用 return 语句。如果函数主体由返回 8.2 之后的无副作用的表达式的单个语句组成,则可以省略 return 。

// good
[1, 2, 3].map((x) => {
  const y = x + 1
  return x * y
})
// good
[1, 2, 3].map((x) => x + 1)
// bad - no returned value means `acc` becomes undefined after the first iteration
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  const flatten = acc.concat(item)
})
// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  const flatten = acc.concat(item)
  return flatten
})
// 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
})

# 3.8 如果数组有多行,则在打开和关闭数组括号之后使用换行符。

// bad
const arr = [
  [0, 1], [2, 3], [4, 5],
]
const objectInArray = [{
  id: 1,
}, {
  id: 2,
}]
const numberInArray = [
  1, 2,
]
// good
const arr = [[0, 1], [2, 3], [4, 5]]
const objectInArray = [
  {
    id: 1,
  },
  {
    id: 2,
  },
]
const numberInArray = [
  1,
  2,
]

# 解构 Destructuring

# 4.1 在访问和使用对象的多个属性时,请使用对象分解。

为什么?解构使您不必为这些属性创建临时引用。

// 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}`
}

# 4.2 使用数组解构。

const arr = [1, 2, 3, 4]
// bad
const first = arr[0]
const second = arr[1]
// good
const [first, second] = arr

# 4.3 将对象分解用于多个返回值,而不是数组分解。

为什么?您可以随时间添加新属性或更改事物顺序,而不会中断呼叫站点。

// bad
function processInput(input) {
  // then a miracle occurs
  return [left, right, top, bottom]
}
// the caller needs to think about the order of return data
const [left, __, top] = processInput(input)
// good
function processInput(input) {
  // then a miracle occurs
  return { left, right, top, bottom }
}
// the caller selects only the data they need
const { left, top } = processInput(input)

# 5 字符串 Strings

# 5.1 '' 对字符串使用单引号。

// bad
const name = "Capt. Janeway"
// bad - template literals should contain interpolation or newlines
const name = `Capt. Janeway`
// good
const name = 'Capt. Janeway'

# 5.2 导致该行超过 100 个字符的字符串不应使用字符串连接跨多行写入。

为什么?破碎的字符串很痛苦,使代码难以搜索。

// bad
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.'
// bad
const errorMessage = 'This is a super long error that was thrown because ' +
  'of Batman. When you stop to think about how Batman had anything to do ' +
  'with this, you would get nowhere fast.'
// good
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'

# 5.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}?`
}

# 5.4 切勿 eval () 在字符串上使用,它会打开太多漏洞。

# 5.5 不要不必要地转义字符串中的字符。

为什么?反斜杠会损害可读性,因此仅在必要时才应使用反斜杠。

// bad
const foo = '\'this\' \i\s \"quoted\"'
// good
const foo = '\'this\' is "quoted"'
const foo = `my name is '${name}'`

# 6 方法 Functions

# 6.1 用命名函数表达式而不是函数声明。

为什么?悬挂了函数声明,这意味着在文件中定义函数之前很容易 - 太容易 - 对其进行引用。这会损害可读性和可维护性。如果发现函数的定义足够大或过于复杂,以至于妨碍了对文件其余部分的理解,那么也许是时候将其提取到自己的模块中了!无论是否从包含变量中推断出名称,都不要忘记为表达式明确命名(这在现代浏览器中或使用 Babel 等编译器时通常是这种情况)。这消除了有关错误的调用堆栈的任何假设。

// bad
function foo() {
  // ...
}
// bad
const foo = function () {
  // ...
}
// good
// lexical name distinguished from the variable-referenced invocation(s)
const short = function longUniqueMoreDescriptiveLexicalFoo() {
  // ...
}

# 6.2 将立即调用的函数表达式包装在括号中

为什么?立即调用的函数表达式是一个单元 - 将其及其包装的调用 paren 封装在 parens 中,以整洁的方式表示出来。请注意,在一个无处不在的模块的世界中,几乎不需要 IIFE。

// immediately-invoked function expression (IIFE)
(function () {
  console.log('Welcome to the Internet. Please follow me.')
}())

# 6.3 从不在非功能块(声明一个函数 if,while 等等)。而是将函数分配给变量。浏览器将允许您执行此操作,但是它们都以不同的方式解释它,这是个坏消息。

# 6.4 注意: ECMA-262 将 a 定义 block 为语句列表。函数声明不是语句。

// bad
if (currentUser) {
  function test() {
    console.log('Nope.')
  }
}
// good
let test
if (currentUser) {
  test = () => {
    console.log('Yup.')
  }
}

# 6.5 永远不要命名参数 arguments。这将优先于 arguments 分配给每个功能范围的对象。

// bad
function foo(name, options, arguments) {
  // ...
}
// good
function foo(name, options, args) {
  // ...
}

# 6.6 从不使用 arguments,... 而是改用 rest 语法。

为什么?... 明确说明您要提取的参数。另外,其余参数是一个实际的 Array,而不仅仅是 like 这样的 Array arguments。

// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments)
  return args.join('')
}
// good
function concatenateAll(...args) {
  return args.join('')
}

# 6.7 使用默认参数语法而不是对函数参数进行突变。

// really bad
function handleThings(opts) {
  // No! We shouldn’t mutate function arguments.
  // Double bad: if opts is falsy it'll be set to an object which may
  // 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 = {}) {
  // ...
}

# 6.8 避免使用默认参数的副作用。

为什么?他们对此感到困惑。

var b = 1
// bad
function count(a = b++) {
  console.log(a)
}
count()  // 1
count()  // 2
count(3) // 3
count()  // 3

# 6.9 始终将默认参数放在最后。

// bad
function handleThings(opts = {}, name) {
  // ...
}
// good
function handleThings(name, opts = {}) {
  // ...
}

# 6.10 切勿使用 Function 构造函数创建新函数。

为什么?以这种方式创建函数的评估字符串类似于 eval (),这将打开漏洞。

// bad
var add = new Function('a', 'b', 'return a + b')
// still bad
var subtract = Function('a', 'b', 'return a - b')

# 6.11 功能签名中的间距。

为什么?一致性很好,添加或删除名称时不必添加或删除空格。

// bad
const f = function(){}
const g = function (){}
const h = function() {}
// good
const x = function () {}
const y = function a() {}

# 6.12 请勿更改参数。

为什么?操纵作为参数传入的对象可能会在原始调用方中导致不必要的变量副作用。

// bad
function f1(obj) {
  obj.key = 1
}
// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1
}

# 6.13 切勿重新分配参数。

为什么?重新分配参数可能会导致意外行为,尤其是在访问 arguments 对象时。它还可能导致优化问题,尤其是在 V8 中。

// 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) {
  // ...
}

# 6.14 优先使用散布运算符... 来调用可变参数函数。

为什么?它更干净,您无需提供上下文,也无法轻松地 new 与组合 apply。

// bad
const x = [1, 2, 3, 4, 5]
console.log.apply(console, x)
// good
const x = [1, 2, 3, 4, 5]
console.log(...x)
// bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]))
// good
new Date(...[2016, 8, 5])

# 6.15 具有多行签名或调用的函数应像本指南中的其他所有多行列表一样缩进:每个项目单独一行,最后一个项目后跟逗号。

// bad
function foo(bar,
             baz,
             quux) {
  // ...
}
// good
function foo(
  bar,
  baz,
  quux,
) {
  // ...
}
// bad
console.log(foo,
  bar,
  baz)
// good
console.log(
  foo,
  bar,
  baz,
)

# 7 方法 Functions

# 7.1 当您必须使用匿名函数时(如传递内联回调时),请使用箭头函数符号。

为什么?它创建函数的版本,该版本在的上下文中执行,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
})

# 7.2 如果函数主体由返回无副作用的表达式的单个语句组成,则省略花括号并使用隐式返回。否则,请保留括号并使用 return 语句。

为什么?语法糖。将多个功能链接在一起时,它读起来很好。

// bad
[1, 2, 3].map((number) => {
  const nextNumber = number + 1
  `A string containing the ${nextNumber}.`
})
// good
[1, 2, 3].map((number) => `A string containing the ${number + 1}.`)
// good
[1, 2, 3].map((number) => {
  const nextNumber = number + 1
  return `A string containing the ${nextNumber}.`
})
// good
[1, 2, 3].map((number, index) => ({
  [index]: number,
}))
// No implicit return with side effects
function foo(callback) {
  const val = callback()
  if (val === true) {
    // Do something if callback returns true
  }
}
let bool = false
// bad
foo(() => bool = true)
// good
foo(() => {
  bool = true
})

# 7.3 如果表达式跨越多行,请将其括在括号中以提高可读性。

为什么?它清楚地显示了函数的开始和结束位置。

// bad
['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod,
  )
)
// good
['get', 'post', 'put'].map((httpMethod) => (
  Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod,
  )
))

# 7.4 为了清楚和一致起见,请始终在参数周围加上括号。

为什么?添加或删除参数时,最大程度减少差异流失。

// bad
[1, 2, 3].map(x => x * x)
// good
[1, 2, 3].map((x) => x * x)
// bad
[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!`
))
// 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
})

# 7.5 避免将箭头函数语法(=>)与比较运算符(<=,>=)混淆。

// bad
const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize
// bad
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize
// good
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize)
// good
const itemHeight = (item) => {
  const { height, largeSize, smallSize } = item
  return height <= 256 ? largeSize : smallSize
}

# 7.6 使用隐式返回值强制箭头函数体的位置。

// bad
(foo) =>
  bar
(foo) =>
  (bar)
// good
(foo) => bar
(foo) => (bar)
(foo) => (
   bar
)

# 8 类和构造函数 Classes & Constructors

# 8.1 始终使用 class。避免 prototype 直接操作。

为什么?class 语法更简洁,更容易推理。

// 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
  }
}

# 8.2 使用 extends 继承。

为什么?这是一种继承原型功能而不中断的内置方法 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]
  }
}

# 8.3 方法可以返回 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)

# 8.4 编写自定义 toString () 方法是可以的,只需确保它可以成功运行并且没有副作用。

class Jedi {
  constructor(options = {}) {
    this.name = options.name || 'no name'
  }
  getName() {
    return this.name
  }
  toString() {
    return `Jedi - ${this.getName()}`
  }
}

# 8.5 如果未指定类,则类具有默认构造函数。不需要空的构造函数或仅委托给父类的构造函数。

// 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'
  }
}

# 8.6 避免重复的班级成员。

为什么?重复的类成员声明将默默地选择最后一个 - 几乎可以肯定,重复是一个错误。

// bad
class Foo {
  bar() { return 1 }
  bar() { return 2 }
}
// good
class Foo {
  bar() { return 1 }
}
// good
class Foo {
  bar() { return 2 }
}

# 8.7 this 除非外部库或框架要求使用特定的非静态方法,否则应使用类方法或将其制成静态方法。作为实例方法,应表明其行为根据接收者的属性而有所不同。

// bad
class Foo {
  bar() {
    console.log('bar')
  }
}
// good - this is used
class Foo {
  bar() {
    console.log(this.bar)
  }
}
// good - constructor is exempt
class Foo {
  constructor() {
    // ...
  }
}
// good - static methods aren't expected to use this
class Foo {
  static bar() {
    console.log('bar')
  }
}

# 9 Modules

# 9.1 始终在非标准模块系统上使用模块(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

# 9.2 不要使用通配符导入。

为什么?这可确保您具有单个默认导出。

// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide'
// good
import AirbnbStyleGuide from './AirbnbStyleGuide'

# 9.3 请勿直接从进口货物中出口。

为什么?尽管单线简明扼要,但采用一种清晰的导入方式和一种清晰的导出方式可以使事情保持一致。

// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide'
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide'
export default es6

# 9.4 仅从一个位置的路径导入。

为什么?具有从同一路径导入的多行可能会使代码难以维护。

// bad
import foo from 'foo'
// … some other imports … //
import { named1, named2 } from 'foo'
// good
import foo, { named1, named2 } from 'foo'
// good
import foo, {
  named1,
  named2,
} from 'foo'

# 9.5 不要导出可变绑定。

W 为什么?通常应避免突变,尤其是在导出可变绑定时。尽管在某些特殊情况下可能需要使用此技术,但通常只应导出常量引用。

// bad
let foo = 3
export { foo }
// good
const foo = 3
export { foo }

# 9.6 在具有单个导出功能的模块中,优先使用默认导出而不是命名导出。

为什么?鼓励更多只导出一件事情的文件,这对于可读性和可维护性来说更好。

// bad
export function foo() {}
// good
export default function foo() {}

# 9.7 将所有 imports 放在非导入语句之上。

为什么?由于 imports 被吊起,因此将它们全部保持在顶部可防止出现意外行为。

// bad
import foo from 'foo'
foo.init()
import bar from 'bar'
// good
import foo from 'foo'
import bar from 'bar'
foo.init()

# 9.8 多行导入应像多行数组和对象文字一样缩进。

为什么?花括号与样式指南中的每个其他花括号块遵循相同的缩进规则,尾随逗号也是如此。

// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'
// good
import {
  longNameA,
  longNameB,
  longNameC,
  longNameD,
  longNameE,
} from 'path'

# 9.9 在模块 import 语句中禁止 Webpack loader 语法。

为什么?由于在导入中使用 Webpack 语法,因此会将代码耦合到模块捆绑器。最好在中使用 loader 语法 webpack.config.js。

// bad
import fooSass from 'css!sass!foo.scss'
import barCss from 'style!css!bar.css'
// good
import fooSass from 'foo.scss'
import barCss from 'bar.css'

# 9.10 不要包括 eslint JavaScript 文件扩展名:import/extensions

为什么?包括扩展将阻止重构,并在每个使用者中不适当地对要导入的模块的实现细节进行硬编码。

// bad
import foo from './foo.js'
import bar from './bar.jsx'
import baz from './baz/index.jsx'
// good
import foo from './foo'
import bar from './bar'
import baz from './baz'

# 10 迭代器和生成器 Iterators and Generators

# 10.1 不要使用迭代器。更喜欢 JavaScript 的高阶函数,而不是像 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 (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)

# 10.2 现在不要使用生成器。

为什么?它们不能很好地移植到 ES5。

# 10.3 如果必须使用生成器,或者不考虑我们的建议,请确保生成器的功能签名间距适当。

为什么?function 并且 * 属于相同概念关键字的一部分 - 不是的修饰语 function,function 是唯一的构造,不同于 function。

// bad
function * foo() {
  // ...
}
// bad
const bar = function * () {
  // ...
}
// bad
const baz = function *() {
  // ...
}
// bad
const quux = function*() {
  // ...
}
// bad
function*foo() {
  // ...
}
// bad
function *foo() {
  // ...
}
// very bad
function
*
foo() {
  // ...
}
// very bad
const wat = function
*
() {
  // ...
}
// good
function* foo() {
  // ...
}
// good
const foo = function* () {
  // ...
}

# 11 属性 Properties

# 11.1 访问属性时,请使用点符号。

const luke = {
  jedi: true,
  age: 28,
}
// bad
const isJedi = luke['jedi']
// good
const isJedi = luke.jedi

# 11.2 使用 [] 变量访问属性时,请使用方括号表示法。

const luke = {
  jedi: true,
  age: 28,
}
function getProp(prop) {
  return luke[prop]
}
const isJedi = getProp('jedi')

# 11.3 ** 计算指数时,请使用指数运算符。

// bad
const binary = Math.pow(2, 10)
// good
const binary = 2 ** 10

# 12 变量 Variables

# 12.1 始终使用 const 或 let 声明变量。不这样做将导致全局变量。我们要避免污染全局名称空间。星球船长警告过我们。

// bad
superPower = new SuperPower()
// good
const superPower = new SuperPower()

# 12.2 每个变量或赋值使用一个 const 或 let 声明。

为什么?通过这种方式添加新的变量声明更加容易,而且您不必担心将 a 换成;a , 或引入仅标点符号的 diff。您也可以使用调试器逐步执行每个声明,而不是一次跳过所有声明。

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z'
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
    goSportsTeam = true
    dragonball = 'z'
// good
const items = getItems()
const goSportsTeam = true
const dragonball = 'z'

# 12.3 将所有 consts 分组,然后将所有 lets 分组。

为什么?以后您可能需要根据先前分配的变量之一分配变量时,这很有用。

// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true
// 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

# 12.4 在需要的地方分配变量,但将其放置在合理的位置。

为什么?let 并且 const 是块作用域而不是函数作用域。

// bad - unnecessary function call
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
}

# 12.5 不要链接变量分配。

为什么?链接变量分配会创建隐式全局变量。

// 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`

# 12.6 避免使用一元增减(++,--)。

为什么?根据 eslint 文档,一元增量和减量语句会自动插入分号,并且会因应用程序中的值增减而导致静默错误。使用诸如 num += 1 而不是 num++ 或的语句来改变您的值也更具表现力 num ++。禁止一元增量和减量语句还可以防止无意中对值进行预增 / 预减,这也可能导致程序出现意外行为。

// bad
const array = [1, 2, 3]
let num = 1
num++
--num
let sum = 0
let truthyCount = 0
for (let i = 0 i < array.length i++) {
  let value = array[i]
  sum += value
  if (value) {
    truthyCount++
  }
}
// good
const array = [1, 2, 3]
let num = 1
num += 1
num -= 1
const sum = array.reduce((a, b) => a + b, 0)
const truthyCount = array.filter(Boolean).length

# 12.7 = 在作业之前或之后避免换行。如果您的分配违反 max-len,则将值括在括号中。

为什么?周围的换行符 = 可能会使分配的值变得模糊。

// bad
const foo =
  superLongLongLongLongLongLongLongLongFunctionName()
// bad
const foo
  = 'superLongLongLongLongLongLongLongLongString'
// good
const foo = (
  superLongLongLongLongLongLongLongLongFunctionName()
)
// good
const foo = 'superLongLongLongLongLongLongLongLongString'

# 12.8 禁止使用未使用的变量。

为什么?由于重构不完全,在代码中任何地方声明并没有使用的变量很可能是错误。这样的变量会占用代码中的空间,并可能引起读者的困惑。

// bad
var some_unused_var = 42
// Write-only variables are not considered as used.
var y = 10
y = 5
// A read for a modification of itself is not considered as used.
var z = 0
z = z + 1
// Unused function arguments.
function getX(x, y) {
    return x
}
// good
function getXPlusY(x, y) {
  return x + y
}
var x = 1
var y = a + 2
alert(getXPlusY(x, y))
// 'type' is ignored even if unused because it has a rest property sibling.
// This is a form of extracting an object that omits the specified keys.
var { type, ...coords } = data
// 'coords' is now the 'data' object without its 'type' property.

# 13 Hoisting

# 13.1 var 声明被提升到最接近的封闭函数范围的顶部,而不是赋值。const 和 let 声明被赋予一个所谓的新概念颞盲区(TDZ) 。重要的是要知道为什么 typeof 不再安全。

// we know this wouldn’t work (assuming there
// is no notDefined global variable)
function example() {
  console.log(notDefined) // => throws a ReferenceError
}
// creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
  console.log(declaredButNotAssigned) // => undefined
  var declaredButNotAssigned = true
}
// the interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
  let declaredButNotAssigned
  console.log(declaredButNotAssigned) // => undefined
  declaredButNotAssigned = true
}
// using const and let
function example() {
  console.log(declaredButNotAssigned) // => throws a ReferenceError
  console.log(typeof declaredButNotAssigned) // => throws a ReferenceError
  const declaredButNotAssigned = true
}

# 13.2 匿名函数表达式将提升其变量名,而不是函数赋值。

function example() {
  console.log(anonymous) // => undefined
  anonymous() // => TypeError anonymous is not a function
  var anonymous = function () {
    console.log('anonymous function expression')
  }
}

# 13.3 命名函数表达式将提升变量名,而不是函数名或函数体。

function example() {
  console.log(named) // => undefined
  named() // => TypeError named is not a function
  superPower() // => ReferenceError superPower is not defined
  var named = function superPower() {
    console.log('Flying')
  }
}
// the same is true when the function name
// is the same as the variable name.
function example() {
  console.log(named) // => undefined
  named() // => TypeError named is not a function
  var named = function named() {
    console.log('named')
  }
}

# 13.4 函数声明将提升其名称和函数主体。

function example() {
  superPower() // => Flying
  function superPower() {
    console.log('Flying')
  }
}

# 14 Comparison Operators & Equality

# 14.1 使用 === 和! 过度和!=。

# 14.2 条件语句(例如,if 语句)使用 ToBoolean 抽象方法强制使用强制表达式来评估其表达式,并始终遵循以下简单规则:

  • 对象评估为 true
  • 未定义的结果为 false
  • 空评估为假
  • 布尔值的取值为布尔值
  • 如果 + 0,-0 或 NaN,则数字的计算结果为 false,否则为 true
  • 如果为空字符串,则字符串评估为 false'',否则为 true
if ([0] && []) {
  // true
  // an array (even an empty one) is an object, objects will evaluate to true
}

# 14.3 将快捷方式用于布尔值,但对字符串和数字进行显式比较。

// bad
if (isValid === true) {
  // ...
}
// good
if (isValid) {
  // ...
}
// bad
if (name) {
  // ...
}
// good
if (name !== '') {
  // ...
}
// bad
if (collection.length) {
  // ...
}
// good
if (collection.length > 0) {
  // ...
}

# 14.4 使用大括号来创建块 case 和 default 包含词汇声明条款(例如 let,const,function,和 class)。

为什么?词法声明在整个 switch 块中都是可见的,但是只有在分配时才进行初始化,只有在 case 到达时才进行初始化。当多个 case 子句尝试定义同一事物时,这会导致问题。

// 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 {}
  }
}

# 14.5 三元数不应嵌套,并且通常是单行表达式。

// bad
const foo = maybe1 > maybe2
  ? "bar"
  : value1 > value2 ? "baz" : null
// split into 2 separated ternary expressions
const maybeNull = value1 > value2 ? 'baz' : null
// better
const foo = maybe1 > maybe2
  ? 'bar'
  : maybeNull
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull

# 14.6 避免不必要的三元语句。

// 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

# 14.7 混合运算符时,请将其括在括号中。唯一的例外是标准的算术运算符:+,-,和 ** 因为它们的优先级被广义地理解。我们建议将括起来 / 并 * 放在括号中,因为当它们混合使用时,它们的优先级可能会模棱两可。

为什么?这提高了可读性并阐明了开发人员的意图。

// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0
// bad
const bar = a ** b - 5 % d
// bad
// one may be confused into thinking (a || b) && c
if (a || b && c) {
  return d
}
// bad
const bar = a + b / c * 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

# 15 Blocks

# 15.1 将括号与所有多行块一起使用。

// bad
if (test)
  return false
// good
if (test) return false
// good
if (test) {
  return false
}
// bad
function foo() { return false }
// good
function bar() {
  return false
}

# 15.2 如果您使用带有 if 和的多行块,请与该块的右括号 else 放在 else 同一行 if。

// bad
if (test) {
  thing1()
  thing2()
}
else {
  thing3()
}
// good
if (test) {
  thing1()
  thing2()
} else {
  thing3()
}

# 15.3 如果某个 if 块始终执行一条 return 语句,else 则不需要随后的块。甲 return 在 else if 块中的以下 if 一个包含块 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
  }
}

# 16 Control Statements

# 16.1 如果您的控制语句(if,while 等)过长或超过最大行长,则可以将每个(分组的)条件放到新行中。逻辑运算符应从此行开始。

为什么?在行的开头要求运算符使运算符保持对齐并遵循类似于方法链接的模式。通过更轻松地从视觉上遵循复杂的逻辑,这也提高了可读性。

// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
  thing1()
}
// bad
if (foo === 123 &&
  bar === 'abc') {
  thing1()
}
// bad
if (foo === 123
  && bar === 'abc') {
  thing1()
}
// bad
if (
  foo === 123 &&
  bar === 'abc'
) {
  thing1()
}
// good
if (
  foo === 123
  && bar === 'abc'
) {
  thing1()
}
// good
if (
  (foo === 123 || bar === 'abc')
  && doesItLookGoodWhenItBecomesThatLong()
  && isThisReallyHappening()
) {
  thing1()
}
// good
if (foo === 123 && bar === 'abc') {
  thing1()
}

# 16.2 不要使用选择运算符代替控制语句。

// bad
!isRunning && startRunning()
// good
if (!isRunning) {
  startRunning()
}

# 17 注释 Comments

# 17.1 使用 /** ... */ 的多行注释。

// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
  // ...
  return element
}
// good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
function make(tag) {
  // ...
  return element
}

# 17.2 使用 // 的单行注释。将单行注释放在注释主题上方的换行符上。除非注释位于块的第一行,否则在注释之前放置一个空行。

// 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
}
// also good
function getType() {
  // set the default type to 'no type'
  const type = this.type || 'no type'
  return type
}

# 17.3 在所有注释的开头加一个空格,以使其易于阅读。

// bad
//is current tab
const active = true
// good
// is current tab
const active = true
// bad
/**
 *make() returns a new element
 *based on the passed-in tag name
 */
function make(tag) {
  // ...
  return element
}
// good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
function make(tag) {
  // ...
  return element
}

# 17.4 如果您要指出需要重新研究的问题,或者是否建议解决需要解决的问题,请在注释前面加上注释 FIXME 或 TODO 帮助其他开发人员快速理解。这些与常规评论不同,因为它们是可行的。动作是 FIXME: -- need to figure this out 或 TODO: -- need to implement。

# 17.5 使用 // FIXME: 注释的问题。

class Calculator extends Abacus {
  constructor() {
    super()
    // FIXME: shouldn’t use a global here
    total = 0
  }
}

# 17.6 使用 // TODO: 注释解决问题的办法。

class Calculator extends Abacus {
  constructor() {
    super()
    // TODO: total should be configurable by an options param
    this.total = 0
  }
}

# 18 空格 Whitespace

# 18.1 使用设置为 2 个空格的软标签(空格字符)。

// bad
function foo() {
∙∙∙∙let name
}
// bad
function bar() {
let name
}
// good
function baz() {
∙∙let name
}

# 18.2 在前支架之前放置 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',
})

# 18.3 在控制语句(if,while 等)中,在圆括号前放置 1 个空格。在函数调用和声明中,参数列表和函数名称之间不能留空格。

// bad
if(isJedi) {
  fight ()
}
// good
if (isJedi) {
  fight()
}
// bad
function fight () {
  console.log ('Swooosh!')
}
// good
function fight() {
  console.log('Swooosh!')
}

# 18.4 用空格隔开运算符。

// bad
const x=y+5
// good
const x = y + 5

# 18.5 使用单个换行符结束文件。

// bad
import { es6 } from './AirbnbStyleGuide'
  // ...
export default es6
// bad
import { es6 } from './AirbnbStyleGuide'
  // ...
export default es6↵
// good
import { es6 } from './AirbnbStyleGuide'
  // ...
export default es6↵

# 18.6 制作较长的方法链(两个以上的方法链)时,请使用缩进。使用前导点,强调该行是方法调用,而不是新语句。

// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount()
// bad
$('#items').
  find('.selected').
    highlight().
    end().
  find('.open').
    updateCount()
// good
$('#items')
  .find('.selected')
    .highlight()
    .end()
  .find('.open')
    .updateCount()
// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', `translate(${radius + margin},${radius + margin})`)
    .call(tron.led)
// good
const leds = stage.selectAll('.led')
    .data(data)
  .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
  .append('svg:g')
    .attr('transform', `translate(${radius + margin},${radius + margin})`)
    .call(tron.led)
// good
const leds = stage.selectAll('.led').data(data)

# 18.7 在块之后和下一条语句之前保留空白行。

// bad
if (foo) {
  return bar
}
return baz
// good
if (foo) {
  return bar
}
return baz
// bad
const obj = {
  foo() {
  },
  bar() {
  },
}
return obj
// good
const obj = {
  foo() {
  },
  bar() {
  },
}
return obj
// bad
const arr = [
  function foo() {
  },
  function bar() {
  },
]
return arr
// good
const arr = [
  function foo() {
  },
  function bar() {
  },
]
return arr

# 18.8 不要用空行填充块。

// bad
function bar() {
  console.log(foo)
}
// bad
if (baz) {
  console.log(qux)
} else {
  console.log(foo)
}
// bad
class Foo {
  constructor(bar) {
    this.bar = bar
  }
}
// good
function bar() {
  console.log(foo)
}
// good
if (baz) {
  console.log(qux)
} else {
  console.log(foo)
}

# 18.9 请勿使用多个空行来填充代码。

// bad
class Person {
  constructor(fullName, email, birthday) {
    this.fullName = fullName
    this.email = email
    this.setAge(birthday)
  }
  setAge(birthday) {
    const today = new Date()
    const age = this.getAge(today, birthday)
    this.age = age
  }
  getAge(today, birthday) {
    // ..
  }
}
// good
class Person {
  constructor(fullName, email, birthday) {
    this.fullName = fullName
    this.email = email
    this.setAge(birthday)
  }
  setAge(birthday) {
    const today = new Date()
    const age = getAge(today, birthday)
    this.age = age
  }
  getAge(today, birthday) {
    // ..
  }
}

# 18.10 请勿在括号内添加空格。

// bad
function bar( foo ) {
  return foo
}
// good
function bar(foo) {
  return foo
}
// bad
if ( foo ) {
  console.log(foo)
}
// good
if (foo) {
  console.log(foo)
}

# 18.11 请勿在方括号内添加空格。

// bad
const foo = [ 1, 2, 3 ]
console.log(foo[ 0 ])
// good
const foo = [1, 2, 3]
console.log(foo[0])

# 18.12 在花括号内添加空格。

// bad
const foo = {clark: 'kent'}
// good
const foo = { clark: 'kent' }

# 18.13 避免使用超过 100 个字符(包括空格)的代码行。注意:根据上述规定,长字符串不受此规则约束,并且不应分解。

为什么?这确保了可读性和可维护性。

// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'))
// good
const foo = jsonData
  && jsonData.foo
  && jsonData.foo.bar
  && jsonData.foo.bar.baz
  && jsonData.foo.bar.baz.quux
  && jsonData.foo.bar.baz.quux.xyzzy
// good
$.ajax({
  method: 'POST',
  url: 'https://airbnb.com/',
  data: { name: 'John' },
})
  .done(() => console.log('Congratulations!'))
  .fail(() => console.log('You have failed this city.'))

# 18.14 在同一行中的一个打开的块令牌和下一个令牌之间,要求保持一致的间距。该规则还强制在同一行上的封闭块令牌和先前令牌内保持一致的间距。

// bad
function foo() {return true}
if (foo) { bar = 0}
// good
function foo() { return true }
if (foo) { bar = 0 }

# 18.15 避免在逗号前加空格,并在逗号后加空格。

```javascript`
// bad
var foo = 1,bar = 2
var arr = [1 , 2]

// good
var foo = 1, bar = 2
var arr = [1, 2]


## 18.16 在计算的属性括号内强制使用间距。

```javascript
// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]

// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]

# 18.17 避免在函数及其调用之间留空格。

// bad
func ()
func
()
// good
func()

# 18.18 强制在对象文字属性中的键和值之间保持间距。

// bad
var obj = { foo : 42 }
var obj2 = { foo:42 }
// good
var obj = { foo: 42 }

# 18.19 避免在行尾使用空格。

# 18.20 避免出现多个空行,仅在文件末尾允许一个换行符,并在文件开始时避免换行符。

// bad - multiple empty lines
var x = 1
var y = 2
// bad - 2+ newlines at end of file
var x = 1
var y = 2
// bad - 1+ newline(s) at beginning of file
var x = 1
var y = 2
// good
var x = 1
var y = 2

# 19 逗号 Commas

# 19.1 主要逗号:Nope。

// bad
const story = [
    once
  , upon
  , aTime
]
// good
const story = [
  once,
  upon,
  aTime,
]
// bad
const hero = {
    firstName: 'Ada'
  , lastName: 'Lovelace'
  , birthYear: 1815
  , superPower: 'computers'
}
// good
const hero = {
  firstName: 'Ada',
  lastName: 'Lovelace',
  birthYear: 1815,
  superPower: 'computers',
}

# 19.2 其他尾随逗号: Yup。

为什么?这将导致更干净的 git diff。同样,像 Babel 这样的编译器也将删除已编译代码中的其他尾部逗号,这意味着您不必担心传统浏览器中的尾部逗号问题

// bad - git diff without trailing comma
const hero = {
     firstName: 'Florence',
-    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing']
}
// good - git diff with trailing comma
const hero = {
     firstName: 'Florence',
     lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
}
// bad
const hero = {
  firstName: 'Dana',
  lastName: 'Scully'
}
const heroes = [
  'Batman',
  'Superman'
]
// good
const hero = {
  firstName: 'Dana',
  lastName: 'Scully',
}
const heroes = [
  'Batman',
  'Superman',
]
// bad
function createHero(
  firstName,
  lastName,
  inventorOf
) {
  // does nothing
}
// good
function createHero(
  firstName,
  lastName,
  inventorOf,
) {
  // does nothing
}
// good (note that a comma must not appear after a "rest" element)
function createHero(
  firstName,
  lastName,
  inventorOf,
  ...heroArgs
) {
  // does nothing
}
// bad
createHero(
  firstName,
  lastName,
  inventorOf
)
// good
createHero(
  firstName,
  lastName,
  inventorOf,
)
// good (note that a comma must not appear after a "rest" element)
createHero(
  firstName,
  lastName,
  inventorOf,
  ...heroArgs
)

# 20 分号 Semicolons

# 20.1 Yup。

为什么?当 JavaScript 遇到没有分号的换行符时,它将使用称为自动分号插入的一组规则来确定是否应将该换行符视为语句的结尾,并(如其名称所示)将分号放入您的如果这样的话,在换行符之前输入代码。但是,ASI 包含一些异常行为,如果 JavaScript 错误地解释了换行符,则代码将中断。随着新功能成为 JavaScript 的一部分,这些规则将变得更加复杂。明确终止您的语句并配置短毛猫以捕获缺少的分号将有助于防止您遇到问题。

// bad - raises exception
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => jedi.father = 'vader')
// bad - raises exception
const reaction = "No! That’s impossible!"
(async function meanwhileOnTheFalcon() {
  // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
  // ...
}())
// bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
function foo() {
  return
    'search your feelings, you know it to be foo'
}
// good
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => {
  jedi.father = 'vader'
})
// good
const reaction = "No! That’s impossible!"
(async function meanwhileOnTheFalcon() {
  // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
  // ...
}())
// good
function foo() {
  return 'search your feelings, you know it to be foo'
}
Read more.

# 21 Type Casting & Coercion

# 21.1 在语句的开头执行类型强制。

# 21.2 字符串 Strings

// => this.reviewScore = 9
// bad
const totalScore = new String(this.reviewScore) // typeof totalScore is "object" not "string"
// bad
const totalScore = this.reviewScore + '' // invokes this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString() // isn’t guaranteed to return a string
// good
const totalScore = String(this.reviewScore)

# 21.3 数字: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)

# 21.4 如果出于某种原因您正在做一些疯狂的事情,并且 parseInt 是瓶颈,并且出于性能原因而需要使用 Bitshift ,请在注释中说明原因和原因。

// good
/**
 * parseInt was the reason my code was slow.
 * Bitshifting the String to coerce it to a
 * Number made it a lot faster.
 */
const val = inputValue >> 0

# 21.5 注意:使用位移操作时要小心。数字表示为 64 位值,但是位移位操作始终返回 32 位整数(source)。对于大于 32 位的整数,位移位会导致意外的行为。讨论。最大的有符号 32 位 Int 是 2,147,483,647:

2147483647 >> 0 // => 2147483647
2147483648 >> 0 // => -2147483648
2147483649 >> 0 // => -2147483647

# 21.6 布尔值

const age = 0
// bad
const hasAge = new Boolean(age)
// good
const hasAge = Boolean(age)
// best
const hasAge = !!age

# 22 命名约定 Naming Conventions

# 22.1 避免使用单个字母名称。用您的命名来描述。

// bad
function q() {
  // ...
}
// good
function query() {
  // ...
}

# 22.2 在命名对象,函数和实例时,请使用 camelCase。

// bad
const OBJEcttsssss = {}
const this_is_my_object = {}
function c() {}
// good
const thisIsMyObject = {}
function thisIsMyFunction() {}

# 22.3 仅在命名构造函数或类时使用 PascalCase。

// bad
function user(options) {
  this.name = options.name
}
const bad = new user({
  name: 'nope',
})
// good
class User {
  constructor(options) {
    this.name = options.name
  }
}
const good = new User({
  name: 'yup',
})

# 22.4 请勿使用下划线或前划线。

为什么?JavaScript 在属性或方法方面没有隐私的概念。尽管下划线是表示 “私有” 的通用约定,但实际上,这些属性是完全公开的,因此是您的公共 API 合同的一部分。该约定可能导致开发人员错误地认为更改不会被视为破坏或不需要测试。tl; dr:如果您希望某些东西是 “私有的”,那么它一定不能明显地存在。

// bad
this.__firstName__ = 'Panda'
this.firstName_ = 'Panda'
this._firstName = 'Panda'
// good
this.firstName = 'Panda'
// good, in environments where WeakMaps are available
// see https://kangax.github.io/compat-table/es6/#test-WeakMap
const firstNames = new WeakMap()
firstNames.set(this, 'Panda')

# 22.5 不要保存对的引用 this。使用箭头函数或 Function#bind。

// 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)
  }
}

# 22.6 基本文件名应与默认导出文件名完全匹配。

// file 1 contents
class CheckBox {
  // ...
}
export default CheckBox
// file 2 contents
export default function fortyTwo() { return 42 }
// file 3 contents
export default function insideDirectory() {}
// in some other file
// bad
import CheckBox from './checkBox' // PascalCase import/export, camelCase filename
import FortyTwo from './FortyTwo' // PascalCase import/filename, camelCase export
import InsideDirectory from './InsideDirectory' // PascalCase import/filename, camelCase export
// bad
import CheckBox from './check_box' // PascalCase import/export, snake_case filename
import forty_two from './forty_two' // snake_case import/filename, camelCase export
import inside_directory from './inside_directory' // snake_case import, camelCase export
import index from './inside_directory/index' // requiring the index file explicitly
import insideDirectory from './insideDirectory/index' // requiring the index file explicitly
// good
import CheckBox from './CheckBox' // PascalCase export/import/filename
import fortyTwo from './fortyTwo' // camelCase export/import/filename
import insideDirectory from './insideDirectory' // camelCase export/import/directory name/implicit "index"
// ^ supports both insideDirectory.js and insideDirectory/index.js

# 22.7 导出默认功能时,请使用 camelCase。文件名应与函数名称相同。

function makeStyleGuide() {
  // ...
}
export default makeStyleGuide

# 22.8 在导出构造函数 / 类 / 单例 / 函数库 / 裸对象时使用 PascalCase。

const AirbnbStyleGuide = {
  es6: {
  },
}
export default AirbnbStyleGuide

# 22.9 首字母缩写词和首字母缩写应始终全部大写或全部小写。

为什么?名称是为了提高可读性,而不是为了安抚计算机算法。

// bad
import SmsContainer from './containers/SmsContainer'
// bad
const HttpRequests = [
  // ...
]
// good
import SMSContainer from './containers/SMSContainer'
// good
const HTTPRequests = [
  // ...
]
// also good
const httpRequests = [
  // ...
]
// best
import TextMessageContainer from './containers/TextMessageContainer'
// best
const requests = [
  // ...
]

# 22.10 您可以选择仅在以下情况下将常量大写:(1)已导出,(2)是一个 const(不能重新分配),并且(3)程序员可以相信它(及其嵌套属性)永不更改。

为什么?这是一个辅助工具,可在程序员不确定变量是否会发生变化的情况下提供帮助。UPPERCASE_VARIABLES 让程序员知道他们可以信任变量(及其属性)不变。

  • 那所有 const 变量呢?- 这是不必要的,因此大写不应用于文件中的常量。但是,应将其用于导出的常量。
  • 导出的对象呢?- 在导出的顶层使用大写字母(例如 EXPORTED_OBJECT.key),并保持所有嵌套属性不变。
// bad
const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file'
// bad
export const THING_TO_BE_CHANGED = 'should obviously not be uppercased'
// bad
export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables'
// ---
// allowed but does not supply semantic value
export const apiKey = 'SOMEKEY'
// better in most cases
export const API_KEY = 'SOMEKEY'
// ---
// bad - unnecessarily uppercases key while adding no semantic value
export const MAPPING = {
  KEY: 'value'
}
// good
export const MAPPING = {
  key: 'value'
}

# 23 存取器 Accessors

# 23.1 不需要属性的访问器功能。

# 23.2 请勿使用 JavaScript getter /setter,因为它们会导致意外的副作用,并且更难以测试,维护和推论。相反,如果您确实使访问器函数,请使用 getVal () 和 setVal ('hello')。

// bad
class Dragon {
  get age() {
    // ...
  }
  set age(value) {
    // ...
  }
}
// good
class Dragon {
  getAge() {
    // ...
  }
  setAge(value) {
    // ...
  }
}

# 23.3 如果属性 / 方法是 a boolean,请使用 isVal () 或 hasVal ()。

// bad
if (!dragon.age()) {
  return false
}
// good
if (!dragon.hasAge()) {
  return false
}

# 23.4 可以创建 get () 和 set () 运行,但要保持一致。

class Jedi {
  constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue'
    this.set('lightsaber', lightsaber)
  }
  set(key, val) {
    this[key] = val
  }
  get(key) {
    return this[key]
  }
}

# 24 Events

# 24.1 在将数据有效负载附加到事件(无论是 DOM 事件还是诸如 Backbone 事件之类的更专有的东西)时,请传递对象文字(也称为 “哈希”)而不是原始值。这允许后续的参与者将更多数据添加到事件有效负载,而无需查找和更新事件的每个处理程序。例如,instead of

// bad
$(this).trigger('listingUpdated', listing.id)
// ...
$(this).on('listingUpdated', (e, listingID) => {
  // do something with listingID
})
prefer:
// good
$(this).trigger('listingUpdated', { listingID: listing.id })
// ...
$(this).on('listingUpdated', (e, data) => {
  // do something with data.listingID
})

# 25 jQuery

# 25.1 用前缀 jQuery 对象变量 $。

// bad
const sidebar = $('.sidebar')
// good
const $sidebar = $('.sidebar')
// good
const $sidebarBtn = $('.sidebar-btn')

# 25.2 缓存 jQuery 查询。

// bad
function setSidebar() {
  $('.sidebar').hide()
  // ...
  $('.sidebar').css({
    'background-color': 'pink',
  })
}
// good
function setSidebar() {
  const $sidebar = $('.sidebar')
  $sidebar.hide()
  // ...
  $sidebar.css({
    'background-color': 'pink',
  })
}

# 25.3 对于 DOM 查询,请使用 Cascading $('.sidebar ul') 或 parent> child $('.sidebar > ul')

# 25.4 使用 find 与范围的 jQuery 对象的查询。

// bad
$('ul', '.sidebar').hide()
// bad
$('.sidebar').find('ul').hide()
// good
$('.sidebar ul').hide()
// good
$('.sidebar > ul').hide()
// good
$sidebar.find('ul').hide()

# 26 ECMAScript 5 兼容性 ECMAScript 5 Compatibility

# 26.1 请参阅 Kangax 的 ES5 兼容性表

# 27 Standard Library

该标准库 包含了实用程序在功能上打破,但仍遗留原因。

# 27.1 使用 Number.isNaN 代替 global 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

# 27.2 使用 Number.isFinite 而不是 global isFinite。

为什么?全局将 isFinite 非数字强制为数字,对于任何强制为有限数字的值,都返回 true。如果需要此行为,请使其明确。

// bad
isFinite('2e3') // true
// good
Number.isFinite('2e3') // false
Number.isFinite(parseInt('2e3', 10)) // true