Js规范

每个人都有自己的开发风格,然而在实际开发过程中,基本很少有项目只需一人独立完成,大部分情况下都需要团队之间的合作,此时,制定一种主流风格便十分重要。它不仅有利于合作开发,增强代码的可读性,还能避免编程过程中一些常犯的错误;更能帮助开发人员养成良好的编程习惯。

standard 规范

我们约定前端代码规范使用standard代码规范:

1、缩进使用两个空格;
2、字符串使用单引号,除非是为了避免转义;
3、不出现未使用的变量
4、关键字后有一个空格
5、函数参数列表的括号前有一个空格
6、始终用===,不使用==
7、中辍操作符前后要有一个空格。
8、逗号后面有一个空格、
9、else与它的大括号同行
10、if语句如果包含多个执行体语句则使用大括号;若只有一条执行语句,可并行无括号,也可用大括号,但不可换行无大括号。
11、始终处理函数的err参数
12、浏览器全局变量始终添加前缀window.
13、不要有多个连续空行
14、三元表达式如果是多行,则?和:放在各自的行上
15、var声明,每个声明占一行
16、为了清除的表明它是一个赋值表达式(=),而不是一个等式(===)的误写,用括号包裹条件中的赋值表达式。
17、单行语句块的内侧要有空格
18、变量和函数的名字使用camelCase(驼峰)格式
19、无多余逗号
20、逗号必须放在当前行的末尾
21、. 应当与属性同行
22、文件以空行结尾
23、函数名字和调用括号之间没有空格
24、键名和键值之间要有空格
25、构造函数的名字以大写字母开始
26、没有参数的构造函数在调用时必须有括号
27、对象若定义了setter,则必须定义相应的getter
28、子类的构造器必须调用super
29、使用对象字面量,不使用对象构造函数
30、不使用argument.callee 和 argument.caller
31、不要给class赋值
32、不要修改由const声明的变量
33、在条件句中不要使用常亮,循环语句除外
34、正则表达式不要使用控制字符
35、不使用debugger语句
36、不要对变量使用delete操作符
37、函数定义无重复参数
38、class定义无重复成员
39、对象字面量无重复键名
40、switch语句无重复case语句

语言规范

ECMAScript 6 简称 ES6,是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ECMAScript 和 JavaScript 的关系:前者是后者的语法规格,后者是前者的一种实现

目前H5和小程序都已经全面支持ES6语法,规范也应该使用ES6标准去约束

引用

constlet 都是块级作用域,var是函数级作用域,项目中使用到es6的地方,基本不会使用到var,记住一个规则,能用const的地方不要用let,能用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
}
  • 将所有的 const 和 let 分组
// bad
let a
const b
let c
const d
let e
// good
const b
const d
let a
let c
let e

对象

  • 请使用字面量值创建对象
// bad
const a = new Object{}
// good
const a = {}
  • 不要使用保留字作为对象的键值,会有浏览器兼容问题
// 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
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 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
// good
[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
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}`

函数

  • 尽量使用函数声明,而不是函数表达式,除非是一些共用的utils
// 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'
  • 使用 class,避免直接操作 prototype
  • 使用 class,避免直接操作 prototype
  • 使用 class,避免直接操作 prototype
  • 使用 class,避免直接操作 prototype
  • 使用 class,避免直接操作 prototype

注释

慷慨的写注释。留下一些供需要理解你做了什么的人们(可能是你自己)下次阅读的信息是有用的。注释应该书写良好和清晰,团队预订JS注释按照 JSDOC 规范约束

让注释有意义。更多的关注于不能马上可见的东西。不要用如下内容浪费读者的时间:

i = 0; // Set i to zero. Webjx.Com

方法内使用单行注释
文件/方法/隔离 使用块注释

命名规范

不允许使用中文或中文拼音方式命名

文件夹与文件命名

文件夹和文件命名小写方式命名,使用-连接单词,单词必须保持独立性,比如userheader这样的命名是不规范的,应该拆分成user-header,eg:

方法名

方法名都以 lowerCamelCase 风格编写。
方法名通常是动词或动词短语。

initXXX

初始化方法,通常运用在vue/小程序中的created/mounted/onLoad/onShow方法中,初始化数据initData,初始化页面initPage

xxxHandle

事件句柄,响应事件的触发,通常用于绑定事件,比如vue中的响应点击购买商品的事件,可以定义为@click="buyGoodsHandle",遵循lowerCamelCase命名方式,以Handle结尾

其他自定义方法命名

方法名通常是动词或动词短语,比如getXXX,setXXX,showXXX,hideXXX,updateXXX,addXXX,resetXXX,clearXXX

常量

常量命名字母全部大写,用下划线分割
如果常量是公用的,应该抽取到consts文件夹中,供其他文件访问
如果只是文件内部使用的话应该声明在所有的import之后的第一行,例如

import { log } from '@/utils/log'
import { INFO } from '@/consts'

const USER_INFO = 'USER_INFO'

控制语句

if-else

if-else一般用来处理不同分支条件的逻辑处理,当逻辑体里面有多个逻辑处理的时候,使用块{}语法处理

if (condition) {
  statementsA;
} else {
  statementsB;
} 

如果仅仅处理一个逻辑,可以不要使用{}块包裹,推荐使用以下语法,比如

if(!isLogin) return
!isLogin && login()
!isLogin && (isLogin = login())

for

  • 尽量避免使用 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)
  • 必要情况下使用for,for语句应该使用如下格式,initialization应该把所有condition的变量声明都定义
for (initialization; condition; update) {
  statements;
}

for (var i = 0, len = data.length; i < len; i++) {
  statements;
}

for (variable in object) {
  statements;
}

for (const good of goods) {
  statements;
}

switch

在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程序将继 续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有,
每个case和switch对齐,这避免了缩进过度。

switch (e­xpression) {
case e­xpressionA: 
  statementsA;
case e­xpressionB: 
  statementsB1;
  statementsB2;
default:
  statements;
}

通常switch使用的场景是case中处理语句块,假如只是简单的单行语句或者赋值语句,直接使用对象来优化处理

let text = ''
switch status {
case 0: 
  text = 'A'
case 1: 
  text = 'B'
default:
  text = '获取不到状态'
}
let statusTextMap = {
  0: 'A',
  1: 'B'
}
let text = statusTextMap(status) || '获取不到状态'