JS代码规范

241 阅读5分钟

编码风格


  • 缩进

强制 使用 4 个空格缩进。eslint: indent. 统一使用 4个空格缩进,不要使用 2个空格或 tab 缩进:

/* bad */
function foo() {
  let name;
}

/* good */
function foo() {
    let name;
}
  • 分号

强制 使用分号。eslint: semi. 统一以分号结束语句,可以避免 JS 引擎自动分号插入机制的怪异行为,在语义上也更加明确。

/* 
* bad - 导致 Uncaught ReferenceError 报错
*/
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => {
    jedi.father = 'vader'
})
 
/* good */
const luke = {};
const leia = {};
[luke, leia].forEach((jedi) => {
    jedi.father = 'vader';
});
 
/* 
* bad - 导致 Uncaught ReferenceError 报错
*/
const reaction = "No! That's impossible!"
(async function meanwhileOnTheFalcon() {
}())
 
/* good */
const reaction = "No! That's impossible!";
(async function meanwhileOnTheFalcon() {
}());

/* 
* bad - 函数将返回 `undefined` 而不是换行后的值
*/
function foo() {
    return
        'Result want to be returned'
}
 
/* good */
function foo() {
    return 'Result want to be returned';
}
  • 逗号
  1. 强制对于用逗号分隔的多行结构,不使用行首逗号。eslint:comma-style
/* bad */
const story = [
    once
  , upon
  , aTime
];
 
/* good */
const story = [
    once,
    upon,
    aTime,
];
 
/* bad */
const hero = {
    firstName: 'Ada'
  , lastName: 'Lovelace'
  , superPower: 'computers'
};
 
/* good */
const hero = {
    firstName: 'Ada',
    lastName: 'Loveplace',
    superPower: 'computers',
};
  1. 强制 对于用逗号分隔的多行结构,始终加上最后一个逗号。eslint:comma-dangle

这样可以使增删行更加容易,也会使 git diffs 更清晰。Babel 等编译器会在编译后的代码里帮我们去掉最后额外的逗号,因此不必担心在旧浏览器中的问题。

/* 
* bad - 没有结尾逗号时,新增一行的 git diff 示例
*/
const hero = {
     firstName: 'Florence',
-    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing']
};

/* 
* good - 有结尾逗号时,新增一行的 git diff 示例
*/
const hero = {
     firstName: 'Florence',
     lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};
  
/* bad */
const hero = {
    firstName: 'Dana',
    lastName: 'Scully'
};

/* good */
const hero = {
    firstName: 'Dana',
    lastName: 'Scully',
};

/* bad */
function createHero(
    firstName,
    lastName,
    inventorOf
) {
  // ...
}
 
createHero(
    firstName,
    lastName,
    inventorOf
);

/* good */
function createHero (
    firstName,
    lastName,
    inventorOf,
) {
  // ...
}

/* 
* good - 需注意,使用扩展运算符的元素后面不能加逗号
*/
function createHero(
    firstName,
    lastName,
    inventorOf,
    ...heroArgs
) {
  // ...
}


  • 【推荐】始终使用大括号包裹代码块。eslint: curly,nonblock-statement-body-position
  1. 多行代码块必须用大括号包裹
/* bad */
if (foo)
    bar();
    baz(); // 这一行并不在 if 语句里

/* good */
if (foo) {
    bar();
    baz();
}
  1. 代码块只有一条语句时,可以省略大括号,并跟控制语句写在同一行。但出于一致性和可读性考虑,不推荐这样做
/* bad */
if (foo)
    return false;

/* 
* bad  - 允许但不推荐
*/
if (foo) return false;
 
/* good */
if (foo) {
    return false;
}
  • 强制对于非空代码块,大括号的换行方式采用 Egyptian Brackets 风格。eslint: brace-style,具体规则如下:
  1. 左大括号 { 前面不换行,后面换行
  2. 右大括号 } 前面换行
  3. 右大括号 } 后面是否换行有两种情况:
    • 3.1 如果 } 终结了整个语句,如条件语句、函数或类的主体,则需要换行
    • 3.2 如果 } 后面存在 else、catch、while 等语句,或存在逗号、分号、右小括号),则不需要换行
/* 
* bad - else 应与 if 的 } 放在同一行
*/
if (foo) {
    thing1();
}
else
    thing2();
}
 
/* good */
if (foo) {
    thing1();
} else {
    thing2();
}
  • 【推荐】对于空代码块,且不在类似 if...else...try..catch..finally.. 的多块结构中时,可以立即将大括号闭合。eslint:brace-style
/* good */
function doNothing() {}
  • 如果空代码块在多块结构中,则仍需要按上一条非空块的规则换行:

/* bad */
if (condition) {
    // …
} else if (otherCondition) {} else {
    // …
}
 
/* good */
if (condition) {
    // …
} else if (otherCondition) {
} else {
    // …
}
 
/* bad */
try {
  // …
} catch (e) {}
 
/* good */
try {
  // …
} catch (e) {
}
  • 强制不要让代码中出现空代码块,这会使阅读者感到困惑。如果必须使用空块,需在块内写明注释。eslint: no-empty
/* bad */
if (condition) {
    thing1();
} else {
}
 
/* good */
if (condition) {
    thing1();
} else {
  // TODO I haven’t determined what to do.
}

空格


  • 强制块的左大括号 { 前有一个空格。eslint:space-before-blocks
/* bad */
function test(){
    console.log('test');
}
 
/* good */
function test() {
    console.log('test');
}
 
/* bad */
dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
 
/* good */
dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
  • 强制控制语句(if、while 等)的左小括号 ( 前有一个空格。声明函数时,函数名和参数列表之间无空格。eslint:keyword-spacing
/* bad */
if(isJedi) {
    fight ();
}
 
/* good */
if (isJedi) {
    fight();
}
 
/* bad */
function fight () {
    console.log ('Swooosh!');
}
 
/* good */
function fight() {
    console.log('Swooosh!');
}
  • 强制小括号内部两侧无空格。eslint: space-in-parens
/* bad */
function bar( foo ) {
    return foo;
}
 
/* good */
function bar(foo) {
    return foo;
}
 
/* bad */
if ( foo ) {
    console.log( foo );
}
 
/* good */
if (foo) {
    console.log(foo);
}
  • 强制方括号内部两侧无空格。eslint: array-bracket-spacing
/* bad */
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
 
/* good */
const foo = [1, 2, 3];
console.log(foo[0]);
  • 强制大括号内部两侧有空格。eslint: object-curly-spacing
/* bad */
const foo = {clark: 'kent'};
 
/* good */
const foo = { clark: 'kent' };
  1. 强制运算符两侧有空格,除了一元运算符。eslint:space-infix-ops
/* bad */
const x=y+5;
 
/* good */
const x = y + 5;
 
/* bad */
const isRight = result === 0? false: true;
 
/* good */
const isRight = result === 0 ? false : true;

/* 
* bad  - 一元运算符与操作对象间不应有空格
*/
const x = ! y;
 
/* good */
const x = !y;
  • 强制定义对象字面量时,不允许所谓的「水平对齐」,即 key、value 之间应该有且只有一个空格。eslint: key-spacing
/* bad */
{
  a           : 'short',
  looooongname: 'long',
}
 
/* good */
{
  a: 'short',
  looooongname: 'long',
}
  • 强制在使用多个(大于两个)方法链式调用时进行换行缩进,把点. 放在前面以强调这是方法调用而不是新语句。eslint:newline-per-chained-call no-whitespace-before-property
/* bad */
$('#items').find('.selected').highlight().end().find('.open').updateCount();
 
/* bad */
$('#items').
  find('.selected').
    highlight().
    end().
  find('.open').
    updateCount();
 
/* good */
$('#items')
  .find('.selected')
    .highlight()
    .end()
  .find('.open')
    .updateCount();
 
/* bad */
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', `translate(${radius + margin},${radius + margin})`)
    .call(tron.led);
 
/* good */
const leds = stage.selectAll('.led')
    .data(data)
  .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
  .append('svg:g')
    .attr('transform', `translate(${radius + margin},${radius + margin})`)
    .call(tron.led);

/* 
* good - 大于 2 个方法的链式调用才需要进行换行
*/
const leds = stage.selectAll('.led').data(data);

空行


  • 【推荐】在文件末尾保留一行空行。eslint: eol-last.统一在文件末尾保留一行空行,即用一个换行符结束文件:
/* 
* bad - 文件末尾未保留换行符
*/
import { foo } from './Foo';
// ...
export default foo;

/* 
* bad - 文件末尾保留了2个换行符
*/
import { foo } from './Foo';
// ...
export default foo;↵
↵

/* good */
import { foo } from './Foo';
// ...
export default foo;↵
  • 强制块的开始和结束不能是空行。eslint: padded-blocks
/* bad */
function bar() {
 
    console.log(foo);
 
}
 
/* good */
function bar() {
    console.log(foo);
}
 
/* bad */
if (baz) {
 
    console.log(qux);
} else {
    console.log(foo);
 
}
 
/* good */
if (baz) {
    console.log(qux);
} else {
    console.log(foo);
}
  • 【参考】在块末和新语句间插入一个空行。
/* 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;

最大字符数和最大行数


  • 【推荐】单行最大字符数:100eslint: max-len.过长的单行代码不易阅读和维护,需要进行合理换行。 单行代码最多不能超过 100 个字符,除了以下两种情况:
    • 字符串和模板字符串
    • 正则表达式
/* bad */
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
 
/* good */
const foo = jsonData
  && jsonData.foo
  && jsonData.foo.bar
  && jsonData.foo.bar.baz
  && jsonData.foo.bar.baz.quux
  && jsonData.foo.bar.baz.quux.xyzzy;
 
/* bad */
$.ajax({ method: 'POST', url: 'https://foo.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
 
/* good */
$.ajax({
    method: 'POST',
    url: 'https://foo.com/',
    data: { name: 'John' },
})
  .done(() => console.log('Congratulations!'))
  .fail(() => console.log('You have failed this city.'));
  • 【推荐】文件最大行数:1000eslint: max-lines 过长的文件不易阅读和维护,最好对其进行拆分。

  • 【推荐】函数最大行数:80eslint: max-lines-per-function 过长的函数不易阅读和维护,最好对其进行拆分。