【最新最全】前端开发规范系列-JS篇

为什么做这件事

目标市面上有很多前端开发规范,有的过于成旧,有的过于简单、片面,好未来培优增长前端团队针对目前这些问题,整理了一份最新最完整的前端开发规范,包括JS、CSS、HTML、lint、Vue等。

做这件事的意义

  1. 提高代码的可读性和可维护性。
    有统计显示,在一个中大型项目中,代码的后期维护成本远高于开发成本。所以,提高代码的可维护性能极大减低项目的维护成本。
  2. 实现项目研发的标准化。
    目前的项目研发流程类似于汽车等工业产品的流水线生产模式,通过开发规范,能有利于研发的标准化。
  3. 减少bug、利于代码审查。
    在艺术的世界里我们提倡天马行空,但是,在代码的世界里,我们要提倡开发规范,这样会减少bug的数量和降低问题的排查时间。

JS篇

1、代码风格

1.1、编码格式

JavaScript 文件使用无 BOM 的 UTF-8 编码。
ps: UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。

1.2、代码缩进

使用 2 个空格缩进

1.3、代码空格

1.3.1、二元运算符两侧各有一个空格,一元运算符与操作对象之间不允许有空格
// bad
let a=12
a=b+c
a ++

// good
let a = 14
a = b + c
a++
1.3.2、代码块起始的左花括号 { 前有一个空格。
// bad
if (condition){
}
while (condition){
}
function funcName(){
}

// good
if (condition) {
}
while (condition) {
}
function funcName() {
}
1.3.3、if / else / for / while / function / switch / do / try / catch / finally 关键字后,有一个空格。
// bad
if(condition) {
}
while(condition) {
}

// good
if (condition) {
}
while (condition) {
}
1.3.4、在对象创建时,属性中的 : 之后有一个空格,: 之前不允许有空格。
// bad
var obj = {
    a : 1,
    b:2,
    c :3
}

// good
var obj = {
    a: 1,
    b: 2,
    c: 3
}

##### 1.3.5、函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格。

// bad
function funcName () {
const func = function (){}
funcName ()


// good
function funcName() {
}
const funcName = function() {
}
funcName()

2、变量声明

2.1、const, let ,var

对所有变量尽量都使用const let ,尽量不要使用var。
ps:这样做可以确保你无法重新分配引用,以避免出现错误和难以理解的代码。
另外将所有的 const 和 let 分组 。 如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。

// bad
var a = 1

// good
const a = 1
const b = 2
let c = 3
let d = 4

2.2、保留字

别使用保留字作为对象的键值,这样在 IE8 下不会运行

// bad
const a = {
  default: {},  // default 是保留字
  common: {}
}

// good
const a = {
  defaults: {},
  common: {}
}

2.3、引号问题

对字符串使用单引号 ,尽量不使用双引号。
ps: 因为大多时候我们的字符串。特别html会出现双引号

// bad
const a = "123"

// good
const a = '123'

2.4、分号问题

以下几种情况结尾可不带分号
声明变量时、import、export、return、throw、break
ps:不带分号简洁明了,并且根据ASI【Automatic Semicolon Insertion】机制,解析时会自动给插入分号

// bad
let a = 12;
import xxx from 'aa';
return;
throw new Error(err);
break;

// good
let a = 12
import xxx from 'aa'
return
throw new Error(err)
break

2.5、链式赋值

变量不要进行链式赋值,变量链式赋值会创建隐藏的全局变量

// bad 
let a = b = 2

// good
let a = 2
let b = 2

2.6、多余变量

不允许出现未被使用的变量,声明但未被使用的变量通常是不完全重构犯下的错误,这种变量在代码里浪费空间并会给读者造成困扰

3、创建变量

3.1、创建对象变量

请使用字面量值创建
ps: 代码量少,易读性高,运行速度快

// bad
const a = new Object()
const b = new Array()

// good
const a = {}
const b = []

3.2、创建函数

不要使用Function构造函数创建函数
ps:代码量少,易读性高,运行速度快

// bad
const add = new Function('a', 'b', 'return a + b')

// good
const func1 = function() {}

3.3、创建字符串变量

字符串太长的时候,建议不要使用字符串连接符换行\,而是使用 +

// bad
const string = '好未来\
    是一家非常好的教育公司\
    是真的非常好'

// good    
const str = '好未来' +
  '是一家非常好的教育公司' +
  '是真的非常好'
 程序化生成字符串时,建议使用模板字符串
const test = 'test'
// bad
const str = 'a' + 'b' + test

// good
const str = `ab${test}`

4、箭头函数

4.1、当你必须使用函数表达式(比如传递匿名函数)时,使用箭头函数标记

ps:它将创建在 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
})

4.2、箭头函数中参数的括号问题

箭头函数如果是只有一个参数时不加括号,简介明了

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

4.3、如果函数体只包含一条没有副作用的返回表达式的语句,可以省略花括号并使用隐式的 return, 否则保留花括号并使用 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}`)

5、解构赋值

ps:解构可以避免创建属性的临时引用

5.1、对象解构

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

// bad
function getFullName(user) {
  const firstName = user.firstName
  const lastName = user.lastName
  return `${firstName} ${lastName}`
}

// good
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`
}

5.2、数组解构

当需要使用数组的多个值时,请同样使用解构赋值

const arr = [1, 2, 3, 4]

// bad
const first = arr[0]
const second = arr[1]

// good
const [first, second] = arr

5.3、函数回传参数的解构

函数需要回传多个值时,请使用对象的解构,而不是数组的解构。
ps: 这样可以非破坏性地随时增加或者改变属性顺序

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

5.4、解构的默认值

解构变量时增加默认值,防止读到空值或者undefined

// bad
const { a, b } = { a: 3 }

// good
const { a = 10, b = 1 } = { a: 3 }

6、类&构造函数

6.1、使用 class,避免直接操作 prototype

ps:容易造成全局污染,造成冲突,定位bug不好定位。

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

6.2、使用 extends 来实现继承

ps优点:清晰,方便,并且这是一个不会破坏 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]
  }
}

7、对象属性

7.1、优先使用 . 来访问对象属性

ps: 好处是简洁,方便

const joke = {
  name: 'haha',
  age: 28
}
// bad
const name = joke['name']

// good
const name = joke.name

7.2、当访问的属性是变量或者数字时只能使用 []

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

8、条件运算

8.1、使用 === 和 !== 而非 == 和 !=

const name = 'test'

// bad
if (name == 'test') {
  // ...
}

// good
if (name === 'test') {
  // ...
}

8.2、尽可能使用简洁的表达式

const name = ''

// bad
if (name === '') {
    // ......
}

// good
if (!name) {
    // ......
}

8.3、如果函数或全局中的 else 块后没有任何语句,可以删除 else

// bad
function getName() {
    if (name) {
       return name;
    } else {
       return 'unnamed';
    }
}

// good
function getName() {
    if (name) {
       return name;
    }
    return 'unnamed';
}

9、eval()

eval 方法比较 evil,所以我们约定禁止使用该方法
ps: eval会引起xss攻击;eval会干扰作用域链等等,不推荐在代码中使用eval

10、不要修改内置对象的原型

11、变量命名规则

11.1、常规变量用小驼峰格式

let userInfo = {}

11.2、全局常量用大写字母,多个字母以下划线连接

const MATH_PI = 3.14

11.3、类内的私有变量以下划线开头

11.4、类名以大驼峰格式

class UserInfo {}

11.5、普通函数名用小驼峰格式

function getUserInfo() {}

11.6、枚举变量用大驼峰格式,枚举属性 使用全部字母大写,单词间下划线分隔

const Week = {
  MONDAY: 0,
  TUESDAY: 1
}

11.7、boolean类型的变量建议使用 is或has开头 【建议】

// 常规变量
const loginStatus = 0
// 全局常量
const COUNT = 0 
// 全局常量
const MAX_NUM = 99 
// 类的声明
class Point {
  constructor(name, age) {
    this._name = name
    this._age = age
  }
  toString() {
    return '(' + this._name + ', ' + this._age + ')'
  }
}
// 普通函数
function stringFormat(source) {
}
// 枚举变量
const TargetState = {
  READING: 1,
  READED: 2,
  APPLIED: 3
}
// boolean类型的变量
const isReady = false
const hasSelected = false

12、&& 和 ||

二元布尔操作符是可短路的, 只有在必要时才会计算到最后一项。

// a++并不会执行
let x = false
let a = 1
let y = x && a++

13、注释规范

13.1、单行注释

独占一行,// 后跟一个空格,缩进与下一行被注释说明的代码一致。

13.2、多行注释

尽量避免使用 /*.../ 这样的多行注释。有多行注释内容时,使用多个单行注释。

13.3、文档化注释格式

使用@key desc格式来书写
常用的关键词有:

@author 作者
@param 参数
@example 示例
@link 链接
@namespace 命名空间
@requires 依赖模块
@return 返回值
@version 版本号

注释的param的类型定义 都是以{ 开始, 以 } 结束
常用的param的类型:{String}, {Number}, {Boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}

13.4、函数/方法的注释

包含函数的说明,有参数和返回值时必须用注释标识

/**
 * @desc 函数描述
 * @param {String} p1 参数1的说明
 * @param {String} p2 参数2的说明
 * @param {Number=} p3 参数3的说明(可选)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
  return {
    p1: p1,
    p2: p2,
    p3: p3
  }
}

你可能感兴趣的