JavaScript编码规范

334 阅读10分钟

1 前言

本文档的目标是使研发中心的各位同学js能够按照统一规范进行编写,使其风格保持一致,便于理解和维护。

2 代码风格

2.1 文件

[建议] 在文件结尾处,保留一个空行。

2.2 结构

2.2.1 缩进

  1. [强制] 使用 2 个空格做为一个缩进层级,不允许使用 4 个空格 或 tab 字符。
  2. [强制] switch 下的 case 和 default 必须增加一个缩进层级。

示例

// good
switch (variable) {

  case '1':
    // do...
    break;

  case '2':
    // do...
    break;

  default:
    // do...

}

// bad
switch (variable) {

case '1':
  // do...
  break;

case '2':
  // do...
  break;

default:
  // do...

}

2.2.2 空格

  1. [强制] 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。

示例

var a = !arr.length;
a++;
a = b + c;
  1. [强制] 用作代码块起始的左花括号 { 前必须有一个空格。

示例

// good
if (condition) {
}

while (condition) {
}

function funcName() {
}

// bad
if (condition){
}

while (condition){
}

function funcName(){
}
  1. [强制] if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。

示例

// good
if (condition) {
}

while (condition) {
}

(function () {
})();

// bad
if(condition) {
}

while(condition) {
}

(function() {
})();
  1. [强制] 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。

示例

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

// bad
var obj = {
  a : 1,
  b:2,
  c :3
};
  1. [强制] 函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格。

示例

// good
function funcName() {
}

var funcName = function funcName() {
};

funcName();

// bad
function funcName () {
}

var funcName = function funcName () {
};

funcName ();
  1. [强制] , 和 ; 前不允许有空格。如果不位于行尾,, 和 ; 后必须跟一个空格。

示例

// good
callFunc(a, b);

// bad
callFunc(a , b) ;
  1. [强制] 在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch 等语句中,() 和 [] 内紧贴括号部分不允许有空格。

示例

// good

callFunc(param1, param2, param3);

save(this.list[this.indexes[i]]);

needIncream && (variable += increament);

if (num > list.length) {
}

while (len--) {
}


// bad

callFunc( param1, param2, param3 );

save( this.list[ this.indexes[ i ] ] );

needIncreament && ( variable += increament );

if ( num > list.length ) {
}

while ( len-- ) {
}
  1. [强制] 单行声明的数组与对象,如果包含元素,{} 和 [] 内紧贴括号部分不允许包含空格

示例

// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = {name: 'obj'};
var obj3 = {
  name: 'obj',
  age: 20,
  sex: 1
};

// bad
var arr1 = [ ];
var arr2 = [ 1, 2, 3 ];
var obj1 = { };
var obj2 = { name: 'obj' };
var obj3 = {name: 'obj', age: 20, sex: 1};
  1. [强制] 行尾不得有多余的空格。

2.2.3 换行

  1. [强制] 每个独立语句结束后必须换行。
  2. [强制] 每行不得超过 120 个字符。(注: 超长的不可分割的代码允许例外,比如复杂的正则表达式。长字符串不在例外之列。)
  3. [强制] 运算符处换行时,运算符必须在新行的行首。

示例

// good
if (user.isAuthenticated()
    && user.isInRole('admin')
    && user.hasAuthority('add-admin')
    || user.hasAuthority('delete-admin')
) {
    // Code
}

var result = number1 + number2 + number3
    + number4 + number5;


// bad
if (user.isAuthenticated() &&
    user.isInRole('admin') &&
    user.hasAuthority('add-admin') ||
    user.hasAuthority('delete-admin')) {
    // Code
}

var result = number1 + number2 + number3 +
    number4 + number5;
  1. [强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行。

示例

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

foo(
  aVeryVeryLongArgument,
  anotherVeryLongArgument,
  callback
);


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

foo(
  aVeryVeryLongArgument
  , anotherVeryLongArgument
  , callback
);
  1. [建议] 不同行为或逻辑的语句集,使用空行隔开,更易阅读。

示例

// 仅为按逻辑换行的示例,不代表setStyle的最优实现
function setStyle(element, property, value) {
  if (element == null) {
    return;
  }

  element.style[property] = value;
}
  1. [建议] 在语句的行长度超过 120 时,根据逻辑条件合理缩进。
  2. [建议] 对于 if...else...、try...catch...finally 等语句,推荐使用在 } 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。

示例

if (condition) {
  // some statements;
}
else {
  // some statements;
}

try {
  // some statements;
}
catch (ex) {
  // some statements;
}

2.2.4 语句

  1. [强制] 不得省略语句结束的分号。
  2. [强制] 在 if / else / for / do / while 语句中,即使只有一行,也不得省略块 {...}。

示例

// good
if (condition) {
  callFunc();
}

// bad
if (condition) callFunc();
if (condition)
    callFunc();
  1. [强制] 函数定义结束不允许添加分号。

示例

// good
function funcName() {
}

// bad
function funcName() {
};

// 如果是函数表达式,分号是不允许省略的。
var funcName = function () {
};

2.3 命名

  1. [强制] 变量 使用驼峰命名法。

示例

var loadingModules = {};
  1. [强制] 常量 使用 全部字母大写,单词间下划线分隔 的命名方式。

示例

var HTML_ENTITY = 333;
  1. [强制] 函数 使用驼峰命名法。

示例

function stringFormat(source) {
}
  1. [强制] 函数的参数 使用驼峰命名法。

示例

function hear(theBells) {
}
  1. [强制] 枚举变量 使用 Pascal命名法,枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。

示例

var TargetState = {
  READING: 1,
  READED: 2,
  APPLIED: 3,
  READY: 4
};
  1. [建议] 函数名 使用 动宾短语。

示例

function getStyle(element) {
}

7.[建议] boolean 类型的变量使用 is 或 has 开头。

示例

var isReady = false;
var hasMoreCommands = false;

2.4 注释

2.4.1 单行注释

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

2.4.2 多行注释

[建议] 避免使用 / * ... */ 这样的多行注释。有多行注释内容时,使用多个单行注释。

2.4.3 文档化注释

[强制] 为了便于代码阅读和自文档化,以下内容必须包含以 /**...*/ 形式的块注释中。 [强制] 文档注释前必须空一行。

  1. 文件
  2. namespace
  3. 函数或方法
  4. 类属性
  5. 事件
  6. 全局变量
  7. 常量
  8. AMD 模块

2.4.4 文件注释

[强制] 文件顶部必须包含文件注释,用 @file 标识文件说明。 [建议] 文件注释中可以用 @author 标识开发者信息。

示例

/**
 * @file Describe the file
 * @author author-name(mail-name@domain.com)
 *         author-name2(mail-name2@domain.com)
 */

2.4.5 函数/方法注释

[强制] 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。 [强制] 参数和返回值注释必须包含类型信息,且不允许省略参数的说明。

示例

/**
 * 函数描述
 *
 * @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
    };
}

2.4.6 细节注释

[强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:

  1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
  2. FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。
  3. HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。
  4. XXX: 该处存在陷阱。此时需要对陷阱进行描述。

3 语言特性

3.1 变量

  1. [强制] 变量、函数在使用前必须先定义。(注: 不通过 var 定义变量将导致变量污染全局环境。原则上不建议使用全局变量,)

示例

// good
var name = 'MyName';

// bad
name = 'MyName';
  1. [强制] 每个 var 只能声明一个变量。(注:一个 var 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。)

示例

// good
var hangModules = [];
var missModules = [];
var visited = {};

// bad
var hangModules = [],
    missModules = [],
    visited = {};
  1. [强制] 变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。(注: 变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。)

示例

// good
function kv2List(source) {
  var list = [];

  for (var key in source) {
    if (source.hasOwnProperty(key)) {
      var item = {
        k: key,
        v: source[key]
      };
      
      list.push(item);
    }
  }

  return list;
}

// bad
function kv2List(source) {
  var list = [];
  var key;
  var item;

  for (key in source) {
    if (source.hasOwnProperty(key)) {
      item = {
        k: key,
        v: source[key]
      };

      list.push(item);
    }
  }

  return list;
}

3.2 条件

  1. [强制] 在 Equality Expression 中使用类型严格的 ===。仅当判断 null 或 undefined 时,允许使用 == null。(注: 使用 === 可以避免等于判断中隐式的类型转换。)

示例

// good
if (age === 30) {
  // ......
}

// bad
if (age == 30) {
  // ......
}
  1. [建议] 尽可能使用简洁的表达式。

示例

// 字符串为空

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

// bad
if (name === '') {
  // ......
}
// 字符串非空

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

// bad
if (name !== '') {
  // ......
}
// 数组非空

// good
if (collection.length) {
  // ......
}

// bad
if (collection.length > 0) {
  // ......
}
// 布尔不成立

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

// bad
if (notTrue === false) {
  // ......
}
// null 或 undefined

// good
if (noValue == null) {
  // ......
}

// bad
if (noValue === null || typeof noValue === 'undefined') {
  // ......
}
  1. [建议] 对于相同变量或表达式的多值条件,用 switch 代替 if。

示例

// good
switch (typeof variable) {
  case 'object':
    // ......
    break;
  case 'number':
  case 'boolean':
  case 'string':
    // ......
    break;
}

// bad
var type = typeof variable;
if (type === 'object') {
    // ......
}
else if (type === 'number' || type === 'boolean' || type === 'string') {
    // ......
}
  1. [建议] 如果函数或全局中的 else 块后没有任何语句,可以删除 else。

示例

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

  return 'unnamed';
}

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

3.3 循环

  1. [建议] 不要在循环体中包含函数表达式,事先将函数提取到循环体外。(注: 循环体中的函数表达式,运行过程中会生成循环次数个函数对象。)

示例

// good
function clicker() {
  // ......
}

for (var i = 0, len = elements.length; i < len; i++) {
  var element = elements[i];
  addListener(element, 'click', clicker);
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
  var element = elements[i];
  addListener(element, 'click', function () {});
}
  1. [建议] 对循环内多次使用的不变值,在循环外用变量缓存。

示例

// good
var width = wrap.offsetWidth + 'px';
for (var i = 0, len = elements.length; i < len; i++) {
  var element = elements[i];
  element.style.width = width;
  // ......
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
  var element = elements[i];
  element.style.width = wrap.offsetWidth + 'px';
  // ......
}
  1. [建议] 对有序集合进行遍历时,缓存 length。(注: 虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。)

示例

// good
for (var i = 0, len = elements.length; i < len; i++) {
  var element = elements[i];
  // ......
}

3.4 类型

3.4.1 类型检测

[建议] 类型检测优先使用 typeof。对象类型检测使用 instanceof。null 或 undefined 的检测使用 == null。

示例

// string
typeof variable === 'string'

// number
typeof variable === 'number'

// boolean
typeof variable === 'boolean'

// Function
typeof variable === 'function'

// Object
typeof variable === 'object'

// RegExp
variable instanceof RegExp

// Array
variable instanceof Array

// null
variable === null

// null or undefined
variable == null

// undefined
typeof variable === 'undefined'

3.4.2 类型转换

  1. [建议] 转换成 string 时,使用 + ''。

示例

// good
num + '';

// bad
new String(num);
num.toString();
String(num);
  1. [建议] 转换成 number 时,通常使用 +。

示例

// good
+str;

// bad
Number(str);
  1. [建议] string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。

示例

var width = '200px';
parseInt(width, 10);
  1. [强制] 使用 parseInt 时,必须指定进制。

示例

// good
parseInt(str, 10);

// bad
parseInt(str);

3.5 字符串

  1. [强制] 字符串开头和结束使用单引号 '。(注: 实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。)

示例

var str = '我是一个字符串';
var html = '<div class="cls">拼接HTML可以省去双引号转义</div>';

3.6 对象

  1. [强制] 使用对象字面量 {} 创建新 Object。

示例

// good
var obj = {};

// bad
var obj = new Object();
  1. [建议] for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。

示例

var newInfo = {}
for (var key in info) {
  if(info.hasOwnProperty(key)){
    newInfo[key] = info[key];
  }
}

3.7 数组

  1. [强制] 使用数组字面量 [] 创建新数组,除非想要创建的是指定长度的数组。

示例

// good
var arr = [];

// bad
var arr = new Array();
  1. [强制] 遍历数组不使用 for in(注:数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果。)

示例

var arr = ['a', 'b', 'c'];

// 这里仅作演示, 实际中应使用 Object 类型
arr.other = 'other things';

// 正确的遍历方式
for (var i = 0, len = arr.length; i < len; i++) {
  console.log(i);
}

// 错误的遍历方式
for (var i in arr) {
  console.log(i);
}

3.8 函数

3.8.1 函数长度

[建议] 一个函数的长度控制在 50 行以内。(注:将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。)

3.8.2 参数设计

  1. [建议] 一个函数的参数控制在 6 个以内,过多参数会导致维护难度增大。
  2. [建议] 通过 options 参数传递非数据输入型参数。