JS 语言规范
语言规范
JavaScript 是一种客户端脚本语言,这里列出了编写 JavaScript 时需要遵守的规则。
类型
-
基本类型
- 字符串
- 数值
- 布尔类型
- null
- undefined
const foo = 1 let bar = foo bar = 9 console.log(foo, bar) // 1, 9
-
复杂类型
- object
- array
- function
const foo = [1, 2, 3] const bar = foo bar[0] = 9 console.log(foo[0], bar[0]) // 9, 9
引用
const
和 let
都是块级作用域,var
是函数级作用域
-
对所有引用都使用
const
,不要使用var
// bad var a = 1 var b = 2 // good const a = 1 const b = 2
-
如果引用是可变动的,则使用
let
// bad var count = 1 if (count < 10) { count += 1 } // good let count = 1 if (count < 10) { count += 1 }
对象
-
请使用字面量值创建对象
// bad const a = new Object{} // good const a = {}
-
别使用保留字作为对象的键值,这样在 IE8 下不会运行
// bad const a = { default: {}, // default 是保留字 common: {} } // good const a = { defaults: {}, common: {} }
-
请使用对象方法的简写方式
// bad const item = { value: 1, addValue: function (val) { return item.value + val } } // good const item = { value: 1, addValue(val) { return item.value + val } }
-
请使用对象属性值的简写方式
const job = 'FrontEnd' // bad const item = { job: job } // good const item = { job }
-
对象属性值的简写方式要和声明式的方式分组
const job = 'FrontEnd' const department = 'JDC' // bad const item = { sex: 'male', job, age: 25, department } // good const item = { job, department, sex: 'male', age: 25 }
数组
-
请使用字面量值创建数组
// bad const items = new Array() // good const items = []
-
向数组中添加元素时,请使用
push
方法const items = [] // bad items[items.length] = 'test' // good items.push('test')
-
使用拓展运算符
...
复制数组// bad const items = [] const itemsCopy = [] const len = items.length let i // bad for (i = 0; i < len; i++) { itemsCopy[i] = items[i] } // good itemsCopy = [...items]
-
使用数组的
map
等方法时,请使用return
声明,如果是单一声明语句的情况,可省略return
// bad [1, 2, 3].map(x => { const y = x + 1 return x * y }) // good [1, 2, 3].map(x => x + 1) // bad const flat = {} [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item) flat[index] = flatten }) // good const flat = {} [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item) flat[index] = flatten 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 })
解构赋值
-
当需要使用对象的多个属性时,请使用解构赋值
// 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}` } // better 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 function doSomething () { return [top, right, bottom, left] } // 如果是数组解构,那么在调用时就需要考虑数据的顺序 const [top, xx, xxx, left] = doSomething() // good function doSomething () { return { top, right, bottom, left } } // 此时不需要考虑数据的顺序 const { top, left } = doSomething()
字符串
-
字符串统一使用单引号的形式
''
// bad const department = "JDC" // good const department = 'JDC'
-
字符串太长的时候,请不要使用字符串连接符换行
\
,而是使用+
const str = '凹凸实验室 凹凸实验室 凹凸实验室' + '凹凸实验室 凹凸实验室 凹凸实验室' + '凹凸实验室 凹凸实验室'
-
程序化生成字符串时,请使用模板字符串
const test = 'test' // bad const str = ['a', 'b', test].join() // bad const str = 'a' + 'b' + test // good const str = `ab${test}`
函数
-
请使用函数声明,而不是函数表达式
// bad const foo = function () { // do something } // good function foo () { // do something }
-
不要在非函数代码块中声明函数
// bad if (isUse) { function test () { // do something } } // good let test if (isUse) { test = () => { // do something } }
-
不要使用
arguments
,可以选择使用...
arguments
只是一个类数组,而...
是一个真正的数组// bad function test () { const args = Array.prototype.slice.call(arguments) return args.join('') } // good function test (...args) { return args.join('') }
-
不要更改函数参数的值
// bad function test (opts) { opts = opts || {} } // good function test (opts = {}) { // ... }
原型
-
使用
class
,避免直接操作prototype
// 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 } }
模块
-
使用标准的 ES6 模块语法
import
和export
// bad const util = require('./util') module.exports = util // good import Util from './util' export default Util // better import { Util } from './util' export default Util
-
不要使用
import
的通配符*
,这样可以确保你只有一个默认的 export// bad import * as Util from './util' // good import Util from './util'
迭代器
-
不要使用
iterators
const numbers = [1, 2, 3, 4, 5] // bad let sum = 0 for (let num of numbers) { sum += num } // good let sum = 0 numbers.forEach(num => sum += num) // better const sum = numbers.reduce((total, num) => total + num, 0)
对象属性
-
使用
.
来访问对象属性const joke = { name: 'haha', age: 28 } // bad const name = joke['name'] // good const name = joke.name
变量声明
-
声明变量时,请使用
const
、let
关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这里推荐使用const
来声明变量,我们需要避免全局命名空间的污染。// bad demo = new Demo() // good const demo = new Demo()
-
将所有的
const
和let
分组// bad let a const b let c const d let e // good const b const d let a let c let e
注释:
1.单行注释:
两个斜杠 // 可以创建一个单行注释,斜杠后面要增加一个空格,紧接着是注释内容。
注释的缩进需要与所注释的代码一致,且要位于被注释代码的上面。
代码演示如下:
// w3cschool教程测试函数
function func() {
// 用来存储定时器函数标识
let flag = null;
}
2.多行注释:
/*/ 可以创建多行注释,也就是以 "/" 开始,"*/" 结束,中间是注释内容。
既然是多行注释,自然被注释的内容是可以换行的。
尽量使用单行注释替代多行注释,如果注释函数,推荐使用多行注释。
3.函数的注释:
函数是使用最为频繁的语法结构,相对较为复杂,所以良好的注释对于理解函数的功能非常有必要。
注释格式如下:
/*方法说明
*@method 方法名
*@for 所属类名
*@param{参数类型}参数名 参数说明
*@return {返回值类型} 返回值说明
*/
可以看到在注释的开始于结尾分别是 / 与 /,具体的注释内容前面也带有一个星号,看起来更加整齐。
看一段简单的注释代码实例:
/*函数说明
* @param {string} p1 参数1的说明
* @param {string} p2 参数2的说明,比较长
* 那就换行了.
* @param {number=} p3 参数3的说明(可选)
* @return {Object} 返回值描述
*/
function foo(p1, p2, p3) {
var p3 = p3 || 10;
return {
p1: p1,
p2: p2,
p3: p3
};
}
4.模块注释:
模块注释格式如下:
/* 模块说明
* @module 模块名
*/
/* 类说明
* @class 类名
* @constructor
*/
由于类分为静态类与非静态类,所以 class 需要与 constructor 或者 static 配合使用。
5.注释内容:
知道为什么需要注释,那么也就知道注释应该怎么写。
注释的目的是告诉阅读者不宜察觉或者不易获取到的信息,而不是一目了然的东西。
// 声明一个变量timer
let timer=null;
Hoisting
-
var
存在变量提升的情况,即var
声明会被提升至该作用域的顶部,但是他们的赋值并不会。而const
和let
并不存在这种情况,他们被赋予了 Temporal Dead Zones, TDZfunction example () { console.log(notDefined) // => throws a ReferenceError } function example () { console.log(declareButNotAssigned) // => undefined var declaredButNotAssigned = true } function example () { let declaredButNotAssigned console.log(declaredButNotAssigned) // => undefined declaredButNotAssigned = true } function example () { console.log(declaredButNotAssigned) // => throws a ReferenceError console.log(typeof declaredButNotAssigned) // => throws a ReferenceError const declaredButNotAssigned = true }
-
匿名函数的变量名会提升,但函数内容不会
function example () { console.log(anonymous) // => undefined anonymous() var anonymous = function () { console.log('test') } }
-
命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会
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') } } function example() { console.log(named) // => undefined named() // => TypeError named is not a function var named = function named () { console.log('named') } }
分号
-
我们遵循
Standard
的规范,不使用分号。关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好的
JS
程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。// bad const test = 'good'; (function () { const str = 'hahaha'; })() // good const test = 'good' ;(() => { const str = 'hahaha' })();
标准特性
为了代码的可移植性和兼容性,我们应该最大化的使用标准方法,例如优先使用 string.charAt(3)
而不是 string[3]
eval()
由于 eval
方法比较 evil
,所以我们约定禁止使用该方法
with() {}
由于 with
方法会产生神奇的作用域,所以我们也是禁止使用该方法的
for-in 循环
推荐使用 for in
语法,但是在对对象进行操作时,容易忘了检测 hasOwnProperty(key)
,所以我们启用了 ESLint
的 guard-for-in
选项
对数组进行
for in
的时候,顺序是不固定的
修改内置对象的原型
不要修改内置对象,如 Object
和 Array