浅析Javascript的自动分号插入(ASI)机制
前言
相信从事过C#和Java的大家都知道分号是用作断句(EOS,endofstatement)的,而且必须加分号,否则编译就不通过了。但JavaScript由于存在ASI机制,因此允许我们省略分号。ASI机制不是说在解析过程中解析器自动把分号添加到代码中,而是说解析器除了分号还会以换行为基础按一定的规则作为断句的依据,从而保证解析的正确性。
规范理论
es5标准定义了自动分号插入规则,包括以下三个基本规则加两个前置条件:
前置条件
1、如果插入分号后解析结果是空语句,那么不会自动插入分号。
例子:(空语句,else前不加分好)
if(a>b) elsec=d
2、如果插入分号后它成为for语句头部的两个分号之一,那么不会自动插入分号。
例子:(不会加分号)
for(a;b )
基本规则
左到右解析程序,当遇到一个不符合任何文法产生式的token(叫做违规token(offendingtoken)),那么只要满足下面条件之一就在违规token前面自动插入分号。
1、至少一个LineTerminator分割了违规token和前一个token。
2、违规token是}。
例子:(1、2不符合任何产生式,并且之间存在LineTerminator,因此在违规token2前加了分好,2和}则是因为违规token是}所以加了分号)
{1 2}3
{1 ;2;}3;
左到右解析程序,tokens输入流已经结束,当解析器无法将输入token流解析成单个完整ECMAScript程序,那么就在输入流的结束位置自动插入分号。
对于受限产生式,也就是下面的5个,我们把产生式[noLineTerminatorhere]后面的token叫做受限token,如果在token和受限token间存在了至少一个LineTerminator,那么会在受限token前自动加上token。
受限的产生式只限如下5个:
PostfixExpression:
LeftHandSideExpression[noLineTerminatorhere]++LeftHandSideExpression[noLineTerminatorhere]--
ContinueStatement:
continue[noLineTerminatorhere]Identifier;
BreakStatement:
break[noLineTerminatorhere]Identifier;
ReturnStatement:
return[noLineTerminatorhere]Expression;
ThrowStatement:throw[noLineTerminatorhere]Expression;
归纳
避免ASI带来的问题
1、后缀运算符++或--和它的操作数应该出现在同一行。
2、return或throw语句的表达式开始位置应该和return或throwtoken同一行。
3、break或continue语句的标示符应该和break或continuetoken同一行。
何时加分号
无分号党(懒人党)想要不加分号,那么就需要知道什么时候应该要加分号。网上的一篇文章归纳了NOASI并且会出现错误的几种情况,在这几种情况下我们是要加分号的。下面是对应的描述:
在以([/+-开头的语句前加分号(由于正常写法均不会出现以.,*%作为语句开头,因此只需记住前面5个即可,你看能懒则懒哦)
不过这里只考虑了换行的情况,其实ASI还存在不换行的情况,这就要根据标准里的三条规则行事了!
知道了这点,其实我们就可以省略大部分的分号了。但是也不强求,因为这还是要根据个人习惯以及团队风格走的。
小补充
为什么自执行函数前要加分号?
主要是应对代码合并压缩时,由于缺少分号;带来的错误。知道了上面的规则,在(开头的行前加分号就可以避免错误了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。