JavaScript 代码风格

基本设置

  • 2 空格缩进
  • UTF-8 编码

严格模式

建议打开严格模式,但不要依赖严格模式中语言特性的变化(仅当需要支持 IE8、IE9 时)

  1. 'use strict';

引号

使用单引号,这样可以跟 HTML 的双引号更好的一起工作。

分号

在语句(Statement)的结尾加分号

  1. // 不建议
  2. var fn = function() {
  3. // Long code
  4. } // 没有分号
  5. // 建议
  6. var fn = function() {
  7. // Long code
  8. }; // 这里有分号

以防万一,在可能有坑的地方手工加分号

  1. var f1 = function ff1() {
  2. return function() {
  3. return 1;
  4. };
  5. } // 此处漏写分号
  6. (function() { // 此处调用了上面的ff1,WAT
  7. })();
  8. console.log(f2); // 1
  9. var f2 = function ff2() {
  10. return function() {
  11. return 1;
  12. };
  13. } // 此处漏写分号
  14. // IIFE
  15. ;(function() { // 注意前面的分号
  16. })();
  17. console.log(f2); // function

或者使用 void function() {}() 的写法,见下

空白与格式

在二元和三元运算符的符号与操作数之间添加空格,在非行末的 , ; } 后添加空格,在 { 前添加空格。并在每个逻辑块中间添加空白行。
特别的,在 if、while 等关键字后加空格,与函数调用做区分

  1. // 不推荐
  2. var foo='bar',hello=foo+2,test=true;
  3. function hi(){
  4. // ...
  5. }
  6. if(foo&&hello){
  7. // ...
  8. }else if(foo){
  9. // ...
  10. }else if(! test){
  11. // ...
  12. }
  13. // 推荐
  14. var foo = 'bar';
  15. var hello = foo + 2;
  16. var test = true;
  17. function hi(arg1, arg2) {
  18. // ...
  19. }
  20. if (foo && hello) {
  21. // ...
  22. } else if (foo) {
  23. // ...
  24. } else if (!test) {
  25. // ...
  26. }

注释

使用 // 作为注释符,可以使用 /* */ 作为多行注释符。注释符号与注释内容之间留空,注释的位置尽量放在代码之上:

  1. /*不推荐*/
  2. //不推荐
  3. ; // 不推荐
  4. /* 推荐 */
  5. // 推荐
  6. ;

建议在今后需要完善的代码中注释以 // TODO。请在 TODO 后标注你要做的事

  1. if (condition) {
  2. console.log('尚未实现');
  3. // TODO: condition 成立时需要做额外的工作
  4. }

不要为大括号另开一行

  1. // 不推荐
  2. if (foo)
  3. {
  4. // ...
  5. }
  6. // 推荐
  7. if (foo) {
  8. // ...
  9. }
  10. // 不允许
  11. return
  12. {
  13. a: 1
  14. };
  15. // 一定要
  16. return {
  17. a: 1
  18. };

只有一行语句时允许不带括号,但需把语句紧跟当前行后

  1. if (foo) doSomething();
  2. for (var i = 0; i < 10; i++) doSomething();

语句太长时写成两行,这时请加大括号

  1. // 不推荐
  2. for (var i = 0; i < 10; i++)
  3. doSomething(aaaa, bbbb);
  4. // 推荐
  5. for (var i = 0; i < 10; i++) {
  6. doSomething(aaaa, bbbb);
  7. }
  8. // 因为某些人调试时喜欢注释代码,于是就出现了
  9. for (var i = 0; i < 10; i++)
  10. // doSomething(aaaa, bbbb);

else 时不要另起一行

  1. // 不推荐
  2. if (test) {
  3. things1();
  4. things2();
  5. }
  6. else {
  7. things3();
  8. }
  9. // 推荐
  10. if (test) {
  11. things1();
  12. things2();
  13. } else {
  14. things3();
  15. }

var 语句

使用变量之前必须先定义,不要定义全局变量。

  1. // 变量 undefinedVar 从未定义过
  2. undefinedVar = 1; // 严格模式中报错
  3. console.log(global.undefinedVar); // 1
  4. // 鉴于 js 中 var 的坑,可以在函数头统一定义所有变量(不强制,我也讨厌把变量定义堆一起。。。)
  5. void function () {
  6. for (var i = 0; i < arr.length; ++i) {
  7. (function () {
  8. console.log(i); // undefined
  9. // 很长的代码,你已经忘记了之前做了什么
  10. for (var i = 0; i < 10; ++i) {
  11. // Do some other things
  12. }
  13. })();
  14. }
  15. }();
  16. // 小心 var 与 closure 合用时的坑
  17. var elements = [ div1, div2, div3 ];
  18. for (var i = 0; i < elements.length; ++i) {
  19. elements[i].addEventListener('event', function() {
  20. console.log(i); // 3
  21. });
  22. }

如果变量有初始赋值则使用单独的 var

  1. // 不推荐
  2. var hello = 1, world = 2;
  3. // 推荐
  4. var hello = 1;
  5. var world = 2;
  6. var foo, fee, fxx;

另外强调,不推荐使用下面这种风格的变量定义方式。

  1. // 不推荐
  2. var hello = arr.pop(),
  3. world = arr.pop();
  1. // 不推荐
  2. var hello = arr.pop()
  3. , world = arr.pop();

原因:

  1. 当需要修改变量定义顺序时不容易做整行移动
  2. 过不了 eslint 的 indent 验证

变量的命名

使用以小写字母开头的驼峰命名(camelCase)法:

  1. // 不推荐
  2. var foo_bar = 'hello eleme';
  3. // 推荐
  4. var fooBar = 'hello eleme';

常量大写

  1. // 不推荐
  2. var prefix = 'http://api.ele.me/v1/';
  3. var Prefix = 'http://api.ele.me/v1/'
  4. // 推荐
  5. var PREFIX = 'http://api.ele.me/v1/';

使用字面量

  1. // 不建议
  2. var obj = new Object();
  3. var array = new Array();
  4. // 推荐
  5. var obj = {};
  6. var array = [];
  7. // 鉴于 Array 构造函数的特殊性,不建议
  8. var arr1 = new Array(4, 5, 6); // [4, 5, 6]
  9. // 以免与下面混淆
  10. var arr2 = new Array(4); // [ undefined * 4 ]
  11. // 等价于(不推荐)
  12. var arr3 = [];
  13. arr3.length = 4;
  14. // 等价于(不推荐)
  15. var arr4 = [,,,,];
  16. console.log('0' in arr2, '0' in arr3, '0' in arr4); // false, false, false
  17. // 不推荐
  18. var str = new String('str');
  19. console.log(str === 'str'); // false
  20. var bool = new Boolean(false);
  21. if (bool) console.log('wat'); // wat
  22. // 当真需要使用字面量包装类时,使用显式强制转换(请先三思)
  23. var strObject = Object('str');
  24. strObject.customProperty = someValue;

比较

建议使用 ===/!== 而非 ==/!=

  1. // 不推荐
  2. function foo(a) {
  3. if (a == 123) {
  4. // ...
  5. }
  6. }
  7. // 推荐
  8. function foo(a) {
  9. a = Number(a);
  10. if (a === 123) {
  11. // ...
  12. }
  13. }

== 的规则比较复杂,大家可能记不住。

  1. var a = '';
  2. // false
  3. if (a === 0);
  4. // true
  5. if (a == 0);

对于可能不存在的全局引用可以先做如此判断:

  1. if (typeof localStorage !== 'undefined') {
  2. // 此时访问 localStorage 绝对不会出现引用错误
  3. }

或者

  1. if ('localStorage' in self) {
  2. // 此时访问 localStorage 绝对不会出现引用错误
  3. }

但注意它们的区别

  1. var a = undefined;
  2. // 判断一个全局变量是否有声明
  3. 'a' in self; // true
  4. // 判断一个变量是否为 undefined 并将未声明的引用作为 undefined 处理
  5. typeof a !== 'undefined'; // false

避免无必要的 if 语句、三元运算符

  1. var arr = [1, 2, 3];
  2. // 不推荐
  3. var flag1 = arr.length > 0 ? true : false;
  4. // 不推荐
  5. var flag2;
  6. if (arr.length > 0) {
  7. flag2 = true;
  8. } else {
  9. flag2 = false;
  10. }
  11. // 推荐
  12. var flag3 = arr.length > 0;

合理的格式化三元运算符

  1. // 不推荐
  2. var flag1 = veryLooooooooooonnnnggggggCondition ? resultWhenTruth : resultWhenFalsy;
  3. // 推荐
  4. var flag2 = veryLooooooooooonnnnggggggCondition
  5. ? resultWhenTruth
  6. : resultWhenFalsy;

复杂逻辑中建议使用显式转换

  1. +num === Number(num);
  2. !!bool === Boolean(bool);
  3. str + '' === String(str);
  4. // 特别的
  5. if (bool)
  6. // 等价于
  7. if (Boolean(bool))
  8. // 故
  9. if ([]) console.log('true'); // true
  10. // 而
  11. if ([] === true) console.log('true'); // 无输出
  12. // 另外
  13. if (Boolean(String(false))) console.log('true'); // true
  14. // 这点在保存 localStorage 时需要注意

不要使用 parseInt 做整数转换,如需使用 parseInt,请给它传入第二个参数 10,避免老式浏览器的坑(IE8?)

  1. var floatValue = 123.456;
  2. // 不要
  3. var intValue = parseInt(floatValue);
  4. // 可以用
  5. var intValue2 = floatValue | 0;
  6. // 更显然的
  7. var intValue3 = Math.floor(floatValue);

特别地,使用 parseFloat 做部分转换

  1. // 例如有:
  2. // <div id="div" style="width: 10px"></div>
  3. var divWidth = getComputedStyle(document.getElementById('div')).width; // '10px'
  4. console.log(parseFloat(divWidth)); // 10
  5. console.log(Number(divWidth)); // NaN
  6. console.log(+divWidth); // NaN

函数定义

建议使用表达式来定义函数,而不是函数语句。

  1. // 不推荐
  2. function fee() {
  3. // ...
  4. }
  5. // 推荐
  6. var foo = function() {
  7. // ...
  8. };

因为函数语句是在进入作用域时声明,破坏了程序从上到下的执行顺序。可能出现定义在 return 后的情况。

  1. void function() {
  2. // 此处可以正常使用函数,但逻辑不清晰
  3. foo();
  4. return null;
  5. function foo() {};
  6. }();

只引用一次的函数建议匿名定义,因为名称存在主观因素。

  1. // 不推荐
  2. var foo = function() {
  3. // ...
  4. };
  5. element.onclick = foo;
  6. // 推荐
  7. element.onclick = function() {
  8. // ...
  9. };

自执行函数

  1. // 不推荐
  2. (function() {
  3. // ...
  4. })();
  5. +function() {
  6. // ...
  7. }();
  8. // 推荐
  9. ~function() {
  10. // ...
  11. }();
  12. // 推荐
  13. void function() {
  14. // ...
  15. }();

括号和加号不是上下文无关的,可能受到上文缺分号的影响而出现奇怪的问题,这些问题甚至不会报错,极难调试,所以不推荐此种用法,比如:

  1. var a = 1 // 此处无分号
  2. +function() {
  3. return 2
  4. }();
  5. // 此处 a 的值为 3

避免嵌套过深

可以使用 Promise 解决深层嵌套问题:

  1. // 不推荐
  2. async1(function() {
  3. // TODO 1
  4. async2(function() {
  5. // TODO 2
  6. async3(function() {
  7. // TODO 3
  8. });
  9. });
  10. });
  11. // 推荐
  12. Promise.resolve()
  13. .then(function() {
  14. return new Promise(function(resolve) {
  15. async1(resolve);
  16. });
  17. })
  18. .then(function() {
  19. // TODO 1
  20. return new Promise(function(resolve) {
  21. async2(resolve);
  22. });
  23. })
  24. .then(function() {
  25. // TODO 2
  26. return new Promise(function(resolve) {
  27. async3(resolve);
  28. });
  29. })
  30. .then(function() {
  31. // TODO 3
  32. });

禁止事项

  • 禁止使用未定义的变量
  • 禁止使用 eval,非用不可时可以使用 Function 构造器替代。
  • 禁止使用 with 语句。
  • 禁止在块作用域中使用函数声明语句。
  1. if (true) {
  2. // 禁止
  3. function func1() {
  4. // ...
  5. }
  6. // 允许
  7. var func2 = function() {
  8. // ...
  9. };
  10. }
  • 禁止使用 8 进制词法
  1. // true
  2. if (010 === 8);
  • 禁止使用 arguments 映射
  1. void function(a) {
  2. arguments[0]++;
  3. // 此处 a 为 2
  4. }(1);
  • 禁止使用重名参数
  • 禁止使用保留字做变量名如 interface