JS代码要不要加分号?

5,907

问题背景

前一段日子团队通过ESLint + Prettier +StyleLint刚刚统一了代码规范。我本地通过编辑器设置了保存时执行eslint fix,偶然的发现了一个报错...

根据提示,在每一个[前加一个;,果然没问题了。但是我们团队的代码规则是不需要分号的,我觉得这样很不美观。想知道报错的根本原因,(其实当时怀疑这是prettier的一个BUG),于是就继续探究了下去。

分号的作用

分号也是一个执行语句,叫做空语句,分号是断句用的,每一个分号就是一个程序语句结束的标志。

知乎上有一段比较可爱的代码。

for(var i=1; i<=5; i++) {
    console.log(i)
}
for(var i=1; i<=5; i++);{
    console.log(i)
}

应该很容易看的看出

第一段输出的是1,2,3,4,5

第二段输出的是6

第二段for循环后面加了;后,for循环后面的代码块就和for循环没有关系了。而for循环的代码,全部都执行在了空语句;上面。

接下来再看下一段代码

var a = 4
var b = [1,2,3]
[a]
console.log(b)

这时候输出b的是undefined,并不是我以为的[1,2,3]

原来在代码解析的时候,变成了

var a = 4
var b = [1,2,3][a]
console.log(b)

并没有通过换行符截断代码。

看来随处添加或者删除分号还是有风险的。但是为什么有的地方可以不加,有的地方不加分号却会报错呢?在查询这个问题的时候,发现了一个名词:Automatic Semicolon Insertion (ASI 自动插入分号机制)。下面会描述这个概念。

分号的添加规则

除了以下的语句,JavaScript的每一个语句都应该加分号的。

  • 循环语句: for, while

  • 分支语句: if, switch, try

  • 函数声明 (不是函数表达式)

原来除了这几个特殊的语句之外,所有的语句都应该加分号的,那我们团队的规则不就错了吗😱😱😱?憋捉急,接下来就是ASI登场了。

ASI

自动插入分号机制...噢,顾名思义,那么应该是在代码解析的时候,解析器会自动插入我们忘记的分号呀!

并不是!!!(啪啪啪的打脸声响起👏👏👏)

抽象语法树中并没有分号这个东西。插入分号只是一个概念,意思是解析器除了分号,还会把换行符当作断句的依据,从而保证语句解析的正确性,并不是真的往里插一个分号(当然了,你本地压缩代码的时候,emmm,应该是需要真的插入的)。

但是ASI并不是将所有的换行符都识别成分号,而是通过以下规则。

ASI的插入规则

大前提,ASI的纲领:

  • 以换行为基础。(就是你肯定得有换行符才行)

  • JS解析器是尽量把语句合并成同一行的解析的。只有符合ASI规则的时候,才会加入分号进行断句。

1、新行并入当前行将构成非法语句,自动插入分号。

if(true)
a = 1
console.log(a)
// 解析为
if(true) a = 1;
console.log(a);

这个代码就清楚了解释了纲领。

必须存在换行符。你写成一行if(true) a = 1 console.log(a)直接就报错了。

尽量并为一行。并没有解析成如下代码。

if(true);
a = 1;
console.log(a);

因为第二行并入第一行没有构成非法语句。

2、在continue, return, break, throw后自动插入分号。

这些语句后面的换行符会自动插入分号的。

return
123
// 解析为
return;
123;

这种写法就return了个寂寞。这就是为什么在返回大量代码的时候喜欢让你在 return后面加一个()包裹着你的返回内容。

3、++、--后缀表达式作为新行的开始,在行首自动插入分号

var a = 1
var b = 2
a
++
b
// 解析成了
var a = 1
var b = 2
a;
++b;

如果你本身就想写++b那还好点,但是如果你的本意是a++呢。结果却是b自增了。这也是为什么很多的代码规则里不建议你用++,--,而是用 +=1,-=1

4、当出现一个不允许的行终止符或“}”时,会在其之前插入一个分号。

function(){ a = 1 }
// 解析成了
function(){ a = 1; }

接下来我们就得知道什么情况下不会触发ASI了

就一句

新的一行以(, [, /, +, -, *, %, ,, .开头

因为这些符号开头的话,根据上述的ASI的第一条规则,这一行和上一行并成一行的时候,并不会形成非法语句。例如[]会被识别成属性。()会被识别成函数执行语句。

解决方法就是在新一行的开始,手动加一个分号即可。

ASI会不会影响代码的效率呀

解析的时候应该会有一点点点点点点的影响吧,执行的时候都一样,没啥影响。但是现在项目都是本地打包压缩好的。所以大部分情况下不用考虑这些问题,跟着团队规范走就行了。

那么到底应该加不加分号呢?

上面说的这么多其实和代码规范没有关系,讲的是ASI机制。加不加分号一看个人习惯喜好,二看团队要求。规范上加不加并没有对错之分,但是你都要对ASI了解。

参考列表

个人公众号