JavaScript编码规范

216 阅读7分钟

一、 编码风格

1. 块

  • 多行代码块必须使用大括号包裹
 // bad
 if (isArray) return false
 
 // good
 if (isArray) {
   return false
 }

2. 空格

  • 块的左大括号前面有个空格
 // bad
 function test(){}
 if(isArray){}
 
 // good
 if (isArray) {
   return false
 }
 function test() {
 
 }
  • 小括号中括号内部两侧无空格,大括号内部两侧有空格
// bad
if ( isArray ) {}
const arr=[ 1,2,3 ]
const obj={a:1}

// good
if (isArray) {}
const arr = [1, 2, 3]
const obj = { a: 1 }
  • 运算符两测有空格(除了一元运算符)
// bad
const x=y+1
const isRight=result===0?true:false

// good
const x = y + 1
const isRight = result === 0 ? true : false

// bad 一元运算符与操作对象中间不应该有空格
const x = ! y

// good
const x = !y
  • 定义对象字面量时, key, value 之间有且只有一个空格,不允许所谓的「水平对齐」
// bad 
{
  a:         1,
  b  : 2
}

// good
{
  a: 1,
  b: 2
}

3. 空行

  • 块的开始和结束不能是空行
// bad
function test() {

  return false
  
}

// good
function test() {
  return false
}
  • 在块的末尾和语句间加一个空行
// 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;

二、 语言特性

1. 变量声明

  • 使用letconst去声明变量

从ES6开始, 可以使用letconst去声明变量,块级作用域不会污染全局命名空间,不要使用var

应优先使用 const,只有当变量会被重新赋值时才使用 let

// bad
var test = 1;
var test;

// good
let test = 1;

// bad
let flag = true
if (flag) {
  // ...
}

//如果变量在后期会发生变化使用let 否则使用const

// good
const flag = true;
if (flag) {
  // ...
}
  • letconst归类在一起写,提高代码的整洁性
// bad
let a = 1;
const b = 2;
let c = 3;
const d = 4;

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

2. 原始类型

  • 不要使用new Number/String/Boolean去声明变量,这样会导致变量为Object类型,可能引起bug
const num = new Number(0);
const str = new String('');
const bool = new Boolean(false);

if (num && str && bool) {
    //... 会执行 表示 三个值都为true
}

let a = 0;
let b = '';
let c = false;
if (a || b || c) {
    // 不会执行 表示三个值都为false
}

  • 类型转换

【数字】转换使用NumberparseInt

const str = '1';

// bad
const num = +str;
const num = str >> 0;
const num = new Number(str);

// good
const num = Number(str);
const num = parseInt(str, 10)

【字符串】转换使用String

const num = 1; 
// bad 
const str = new String(num); 
const str = num + '';  
const str = num.toString(); 

// good 
const str = String(num);

【布尔值】使用 !!

const age = 0;

// bad 
const bool = new Boolean(age);
const bool = Boolean(age);

// good
const bool = !!age

3. 数组

  • 不要使用new Array()Array()去创建变量
// bad 
const a = new Array(1, 2, 3);
const b = Array(1, 2, 3);

// good
const a = [1, 2, 3];
const b = new Array(100); // 构建长度为500的空数组
  • 使用扩展运算符 ... 处理数组

数组复制:

const arr = [1, 2, 3];

// bad
const arr2 = arr.map(item => item);

// good
const arr2 = [...arr];

数组拼接:

// bad
const array = [1, 2, 3].concat([4, 5]);

// good
const array = [1, 2, 3, ...[4, 5]];

用 ... 替代 apply

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

// bad
Math.max.apply(Math, arr); 

// good
Math.max(...arr);

使用解构获取数据元素

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

// bad
const A = arr[0];
const B = arr[3];

// good
const [A, , , B] = arr

4. 对象

  • 使用字面量创建对象
// bad 
const obj = new Object();

// good
const obj = {};
  • 使用对象的简写形式
const value = 'test';

// bad
const obj = {
  value: value,
  addValue: function (value) {
    return value + 'add'
  },
}

// good
const obj = {
  value,
  addValue(value) {
    return value + 'add'
  },
}
  • 对象的属性名不要用引号包裹,除非包含特殊字符
// bad
const obj = {
  'a': 1,
  'b': 2,
  'data-a': 3,
  'data-b': 4.
}

// good 
const obj = {
  a: 1,
  b: 2,
  'data-a': 3,
  'data-b': 4.
}
  • 优先使用 . 访问对象的属性, 访问动态属性名或者包含特殊字符时可用[]
const obj = {
  a: 1,
  'data-a': 2,
}

// bad
const a = obj['a']

// good
const a = obj.a;
const dataA = obj['data-a'];
const b = 'b'
obj[b] = 3;
  • 使用扩展运算符 ... 处理对象, 代替Object.assgin()
const test = { a: 1, b: 2 };

//  very bad
const test2 = Object.assgin(test, { c: 3 }) // 会影响到原数据 
test.a = 5;
console.log(test2.a) // 5

// bad
const test2 = Object.assgin({}, test, { c: 3 })

// good
const test2 = {
  ...test,
  c: 3,
}
  • 使用解构获取对象属性
const obj = {
  a: 1,
  b: 2,
}

// bad
const a = obj.a;
const b = obj.b;

// good
const { a, b} = obj;

5. 函数

  • 不要使用Function去创建函数
// bad
const sum = new Function('a', 'b', 'return a + b');

// good
const sum = (a, b) => a + b
  • 不要再块中使用函数声明
// bad
if (true) {
  function test() {
    console.log('test');
  }
}
test()  //在函数外部也可以去使用  会引起误解

// good 
if (true) {
  const test = () => {
    console.log('test')
  }
}
test() // ReferenceError: test is not defined

  • 使用箭头函数替代匿名函数
// bad
[1, 2, 3].map(function(item) {
    return item
});

// good
[1, 2, 3].map(item => item);
  • 不要使用arguments对象

ES6提供了rest操作符...

// bad 
function test(a, b) {
  const args = Array.prototype.slice.call(arguments, test.length);
  console.log(args)
}

// good 
function foo(a, b, ...args) {
  console.log(args); 
} 
foo(1, 2, 3, 4); // => [3, 4]
  • 使用默认参数语法(有默认参数的需要放在参数列表的最后)
// bad
const multiple = (a, b) => {
  a = a || 0;
  b = b || 0; 
  return a * b;
};

// good
const multiple = (a = 0, b = 0) =>{
  return a * b
}
  • 不要修改函数的参数
const obj = { key: 0 }

// bad 
const test = obj => {
    obj.key = 1
};
test(obj);
console.log(obj)  // { key: 1 };

// good
const test = obj => {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
  • 更不要给函数参数重新赋值,这可能导致意外的行为和内核优化问题:
// bad 
function foo(bar, baz) {
  if (!baz) {
    bar = 1;
  } 
} 

// good 
function foo(bar, baz) {
  let qux = bar;
  if (!baz) {
    qux = 1;
  }
}
  • 优先使用 JS 提供的高阶函数进行迭代运算。

如使用 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;
} 
console.log(sum); // => 15;

// good 
let sum = 0; 
numbers.forEach((num) => { sum += num; });
console.log(sum); // => 15;

// best 
const sum = numbers.reduce((total, num) => total + num, 0); 
console.log(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 
const increasedByOne = numbers.map(num => num + 1);

6. 模块

  • 使用 ES6 modules 而非其他非标准的模块系统
// bad
const React = require('react');
module.exports = React.Component;

// good
import React, { Component } from 'react'; 
export default Component;
  • 不要用多个 import 引入同一模块
// bad
import React from 'react'; 
import { Component } from 'react'; 

// good
import React, { Component } from 'react'; 
  • import 语句需要放到模块的最上方
// bad 
import foo from 'foo';
foo.init();

import bar from 'bar'; 
bar.init(); 

// good
import foo from 'foo'; 
import bar from 'bar';
foo.init();
bar.init();
  • import 语句的排序
  • 先 import 第三方模块,再 import 自己工程里的模块
  • 先 import 绝对路径,再 import 相对路径

7. 操作符

  • 使用严格相等运算符
const id = '123';

// bad     == 会转换类型比较(最好使用===)
if (id == 123) {
  // ...
}

// good 

if (Number(id) === 123) {
  // ...
}
  • 不要使用 void 运算符

在很老版本中 undefined 值时可变的,因此使用void语句一般是用来得到一个undefined值,但在新版本中,上面的问题已经不复存在

// bad 
const foo = void 0;

// good
const foo = undefined;
  • 避免嵌套的三元表达式
// bad
const test = a > b ? a : b > c ? b : c

// good
const foo = b > c ? b : c
const test = a > b ? a : foo
  • 避免不必要的三元表达式
// 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;
  • 混合使用多种操作符时,用小括号包裹分组
// bad 
const foo = a && b < 0 || c > 0 || d + 1 === 0;

// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);

8. 注释

  • 注释行上方需要有一个空行(除非注释行上方是一个块的顶部) 注释后面跟一个空格
// bad
if (isDisable) {
  const a = 1;
  // .....
  return a
}

// good
if (isDisable) {
  const a = 1;
  
  // .....
  return a
}
  • 文档类注释使用 jsdoc 规范
/**
 * Book类,代表一个书本. 
 * @constructor 
 * @param {string} title - 书本的标题. 
 * @param {string} author - 书本的作者.
 */
function Book(title, author) {
  this.title = title; 
  this.author = author;
} 
Book.prototype = { 
  /**
   * 获取书本的标题 
   * @returns {string|*} 
   */ 
  getTitle() { 
    return this.title;
  }, 
  
  /** 
   * 设置书本的页数 
   * @param pageNum {number} 页数
   */
  setPageNum(pageNum) {
    this.pageNum = pageNum; 
  }, 
};

9. 命名

  • 使用小驼峰命名原始类型,对象,函数,实例。
// bad
const this_is_my_string = 'foo';
const this_is_my_object = {};
function this_is_my_function() {
  // ...
}

// good
const thisIsMyString = 'foo';
const thisIsMyObject = {};
function thisisMyFunction() {
  // ...
}
  • 使用大驼峰命名类和构造函数
// bad
function user(options) {
  this.name = options.name;
}

// good
function User(options) {
  this.name = options.name
}
  • 全部大写字母&单词间用下划线分割的命名模式 用于声明常量

如果仅仅是单个文件去用的常量,命名按照之前的规范就可以,在很多文件下都需要去使用的常量,需要按照此规范进行

// bad
const PRE_VALUE = '一个常量'
export let PRE_VALUE = '一个常量'

// good
export const PRE_VALUE = '一个常量'