语言规范

JavaScript 是一种客户端脚本语言,这里列出了编写 JavaScript 时需要遵守的规则。

类型

  • 基本类型

    • 字符串
    • 数值
    • 布尔类型
    • null
    • undefined
    1. const foo = 1
    2. let bar = foo
    3. bar = 9
    4. console.log(foo, bar) // 1, 9
  • 复杂类型

    • object
    • array
    • function
    1. const foo = [1, 2, 3]
    2. const bar = foo
    3. bar[0] = 9
    4. console.log(foo[0], bar[0]) // 9, 9

引用

constlet 都是块级作用域,var 是函数级作用域

  • 对所有引用都使用 const,不要使用 var

    1. // bad
    2. var a = 1
    3. var b = 2
    4. // good
    5. const a = 1
    6. const b = 2
  • 如果引用是可变动的,则使用 let

    1. // bad
    2. var count = 1
    3. if (count < 10) {
    4. count += 1
    5. }
    6. // good
    7. let count = 1
    8. if (count < 10) {
    9. count += 1
    10. }

对象

  • 请使用字面量值创建对象

    1. // bad
    2. const a = new Object{}
    3. // good
    4. const a = {}
  • 别使用保留字作为对象的键值,这样在 IE8 下不会运行

    1. // bad
    2. const a = {
    3. default: {}, // default 是保留字
    4. common: {}
    5. }
    6. // good
    7. const a = {
    8. defaults: {},
    9. common: {}
    10. }
  • 请使用对象方法的简写方式

    1. // bad
    2. const item = {
    3. value: 1,
    4. addValue: function (val) {
    5. return item.value + val
    6. }
    7. }
    8. // good
    9. const item = {
    10. value: 1,
    11. addValue(val) {
    12. return item.value + val
    13. }
    14. }
  • 请使用对象属性值的简写方式

    1. const job = 'FrontEnd'
    2. // bad
    3. const item = {
    4. job: job
    5. }
    6. // good
    7. const item = {
    8. job
    9. }
  • 对象属性值的简写方式要和声明式的方式分组

    1. const job = 'FrontEnd'
    2. const department = 'JDC'
    3. // bad
    4. const item = {
    5. sex: 'male',
    6. job,
    7. age: 25,
    8. department
    9. }
    10. // good
    11. const item = {
    12. job,
    13. department,
    14. sex: 'male',
    15. age: 25
    16. }

数组

  • 请使用字面量值创建数组

    1. // bad
    2. const items = new Array()
    3. // good
    4. const items = []
  • 向数组中添加元素时,请使用 push 方法

    1. const items = []
    2. // bad
    3. items[items.length] = 'test'
    4. // good
    5. items.push('test')
  • 使用拓展运算符 ... 复制数组

    1. // bad
    2. const items = []
    3. const itemsCopy = []
    4. const len = items.length
    5. let i
    6. // bad
    7. for (i = 0; i < len; i++) {
    8. itemsCopy[i] = items[i]
    9. }
    10. // good
    11. itemsCopy = [...items]
  • 使用数组的 map 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return

    1. // good
    2. [1, 2, 3].map(x => {
    3. const y = x + 1
    4. return x * y
    5. })
    6. // good
    7. [1, 2, 3].map(x => x + 1)
    8. // bad
    9. const flat = {}
    10. [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    11. const flatten = memo.concat(item)
    12. flat[index] = flatten
    13. })
    14. // good
    15. const flat = {}
    16. [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    17. const flatten = memo.concat(item)
    18. flat[index] = flatten
    19. return flatten
    20. })
    21. // bad
    22. inbox.filter((msg) => {
    23. const { subject, author } = msg
    24. if (subject === 'Mockingbird') {
    25. return author === 'Harper Lee'
    26. } else {
    27. return false
    28. }
    29. })
    30. // good
    31. inbox.filter((msg) => {
    32. const { subject, author } = msg
    33. if (subject === 'Mockingbird') {
    34. return author === 'Harper Lee'
    35. }
    36. return false
    37. })

解构赋值

  • 当需要使用对象的多个属性时,请使用解构赋值

    1. // bad
    2. function getFullName (user) {
    3. const firstName = user.firstName
    4. const lastName = user.lastName
    5. return `${firstName} ${lastName}`
    6. }
    7. // good
    8. function getFullName (user) {
    9. const { firstName, lastName } = user
    10. return `${firstName} ${lastName}`
    11. }
    12. // better
    13. function getFullName ({ firstName, lastName }) {
    14. return `${firstName} ${lastName}`
    15. }
  • 当需要使用数组的多个值时,请同样使用解构赋值

    1. const arr = [1, 2, 3, 4]
    2. // bad
    3. const first = arr[0]
    4. const second = arr[1]
    5. // good
    6. const [first, second] = arr
  • 函数需要回传多个值时,请使用对象的解构,而不是数组的解构

    1. // bad
    2. function doSomething () {
    3. return [top, right, bottom, left]
    4. }
    5. // 如果是数组解构,那么在调用时就需要考虑数据的顺序
    6. const [top, xx, xxx, left] = doSomething()
    7. // good
    8. function doSomething () {
    9. return { top, right, bottom, left }
    10. }
    11. // 此时不需要考虑数据的顺序
    12. const { top, left } = doSomething()

字符串

  • 字符串统一使用单引号的形式 ''

    1. // bad
    2. const department = "JDC"
    3. // good
    4. const department = 'JDC'
  • 字符串太长的时候,请不要使用字符串连接符换行 \,而是使用 +

    1. const str = '凹凸实验室 凹凸实验室 凹凸实验室' +
    2. '凹凸实验室 凹凸实验室 凹凸实验室' +
    3. '凹凸实验室 凹凸实验室'
  • 程序化生成字符串时,请使用模板字符串

    1. const test = 'test'
    2. // bad
    3. const str = ['a', 'b', test].join()
    4. // bad
    5. const str = 'a' + 'b' + test
    6. // good
    7. const str = `ab${test}`

函数

  • 请使用函数声明,而不是函数表达式

    1. // bad
    2. const foo = function () {
    3. // do something
    4. }
    5. // good
    6. function foo () {
    7. // do something
    8. }
  • 不要在非函数代码块中声明函数

    1. // bad
    2. if (isUse) {
    3. function test () {
    4. // do something
    5. }
    6. }
    7. // good
    8. let test
    9. if (isUse) {
    10. test = () => {
    11. // do something
    12. }
    13. }
  • 不要使用 arguments,可以选择使用 ...

    arguments 只是一个类数组,而 ... 是一个真正的数组

    1. // bad
    2. function test () {
    3. const args = Array.prototype.slice.call(arguments)
    4. return args.join('')
    5. }
    6. // good
    7. function test (...args) {
    8. return args.join('')
    9. }
  • 不要更改函数参数的值

    1. // bad
    2. function test (opts) {
    3. opts = opts || {}
    4. }
    5. // good
    6. function test (opts = {}) {
    7. // ...
    8. }

原型

  • 使用 class,避免直接操作 prototype

    1. // bad
    2. function Queue (contents = []) {
    3. this._queue = [..contents]
    4. }
    5. Queue.prototype.pop = function () {
    6. const value = this._queue[0]
    7. this._queue.splice(0, 1)
    8. return value
    9. }
    10. // good
    11. class Queue {
    12. constructor (contents = []) {
    13. this._queue = [...contents]
    14. }
    15. pop () {
    16. const value = this._queue[0]
    17. this._queue.splice(0, 1)
    18. return value
    19. }
    20. }

模块

  • 使用标准的 ES6 模块语法 importexport

    1. // bad
    2. const util = require('./util')
    3. module.exports = util
    4. // good
    5. import Util from './util'
    6. export default Util
    7. // better
    8. import { Util } from './util'
    9. export default Util
  • 不要使用 import 的通配符 *,这样可以确保你只有一个默认的 export

    1. // bad
    2. import * as Util from './util'
    3. // good
    4. import Util from './util'

迭代器

  • 不要使用 iterators

    1. const numbers = [1, 2, 3, 4, 5]
    2. // bad
    3. let sum = 0
    4. for (let num of numbers) {
    5. sum += num
    6. }
    7. // good
    8. let sum = 0
    9. numbers.forEach(num => sum += num)
    10. // better
    11. const sum = numbers.reduce((total, num) => total + num, 0)

对象属性

  • 使用 . 来访问对象属性

    1. const joke = {
    2. name: 'haha',
    3. age: 28
    4. }
    5. // bad
    6. const name = joke['name']
    7. // good
    8. const name = joke.name

变量声明

  • 声明变量时,请使用 constlet 关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这里推荐使用 const 来声明变量,我们需要避免全局命名空间的污染。

    1. // bad
    2. demo = new Demo()
    3. // good
    4. const demo = new Demo()
  • 将所有的 constlet 分组

    1. // bad
    2. let a
    3. const b
    4. let c
    5. const d
    6. let e
    7. // good
    8. const b
    9. const d
    10. let a
    11. let c
    12. let e

Hoisting

  • var 存在变量提升的情况,即 var 声明会被提升至该作用域的顶部,但是他们的赋值并不会。而 constlet 并不存在这种情况,他们被赋予了 Temporal Dead Zones, TDZ

    1. function example () {
    2. console.log(notDefined) // => throws a ReferenceError
    3. }
    4. function example () {
    5. console.log(declareButNotAssigned) // => undefined
    6. var declaredButNotAssigned = true
    7. }
    8. function example () {
    9. let declaredButNotAssigned
    10. console.log(declaredButNotAssigned) // => undefined
    11. declaredButNotAssigned = true
    12. }
    13. function example () {
    14. console.log(declaredButNotAssigned) // => throws a ReferenceError
    15. console.log(typeof declaredButNotAssigned) // => throws a ReferenceError
    16. const declaredButNotAssigned = true
    17. }
  • 匿名函数的变量名会提升,但函数内容不会

    1. function example () {
    2. console.log(anonymous) // => undefined
    3. anonymous()
    4. var anonymous = function () {
    5. console.log('test')
    6. }
    7. }
  • 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会

    1. function example() {
    2. console.log(named) // => undefined
    3. named() // => TypeError named is not a function
    4. superPower() // => ReferenceError superPower is not defined
    5. var named = function superPower () {
    6. console.log('Flying')
    7. }
    8. }
    9. function example() {
    10. console.log(named) // => undefined
    11. named() // => TypeError named is not a function
    12. var named = function named () {
    13. console.log('named')
    14. }
    15. }

分号

  • 我们遵循 Standard 的规范,不使用分号。

    关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好的 JS 程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。

    1. // bad
    2. const test = 'good';
    3. (function () {
    4. const str = 'hahaha';
    5. })()
    6. // good
    7. const test = 'good'
    8. ;(() => {
    9. const str = 'hahaha'
    10. })();

标准特性

为了代码的可移植性和兼容性,我们应该最大化的使用标准方法,例如优先使用 string.charAt(3) 而不是 string[3]

eval()

由于 eval 方法比较 evil,所以我们约定禁止使用该方法

with() {}

由于 with 方法会产生神奇的作用域,所以我们也是禁止使用该方法的

for-in 循环

推荐使用 for in 语法,但是在对对象进行操作时,容易忘了检测 hasOwnProperty(key),所以我们启用了 ESLintguard-for-in 选项

对数组进行 for in 的时候,顺序是不固定的

修改内置对象的原型

不要修改内置对象,如 ObjectArray