JavaScript的自动分号插入(ASI)有哪些规则?
那么,首先我应该问问这是否依赖于浏览器。
我读过,如果find一个无效的标记,但代码段是有效的,直到该无效标记,分号前插入一个换行符插入。
然而,引用由分号插入引起的错误的常见例子是:
return _a+b;
..这似乎不遵循这个规则,因为_a将是一个有效的标志。
另一方面,打破呼叫连锁按预期工作:
$('#myButton') .click(function(){alert("Hello!")});
有没有人有更深入的规则说明?
首先,您应该知道哪些语句受自动分号插入(简称为ASI)的影响:
- 空声明
-
var
语句 - expression语句
-
do-while
声明 -
continue
声明 -
break
陈述 -
return
语句 -
throw
声明
规范中描述了ASI的具体规则:
- §11.9.1自动插入分号的规则
描述了三种情况:
-
遇到语法不允许的令牌(
LineTerminator
或}
)时,如果出现以下情况,则在其之前插入分号:- 令牌与前一个令牌至less由一个
LineTerminator
分开。 - 令牌是
}
例如:
{ 1 2 } 3 // is transformed to { 1 ;2 ;} 3;
NumericLiteral
1
符合第一个条件,以下标记是行终止符。2
满足第二个条件,下面的标记是}
。 - 令牌与前一个令牌至less由一个
-
当遇到令牌的inputstream的结束并且parsing器不能将input令牌streamparsing为单个完整的程序时,则在inputstream的末尾自动插入分号。
例如:
a = b ++c // is transformed to: a = b; ++c;
-
这种情况发生在语法的某些生成允许令牌的情况下,但是生产是限制生产 ,在限制令牌之前自动插入分号。
限制作品:
UpdateExpression : LeftHandSideExpression [no LineTerminator here] ++ LeftHandSideExpression [no LineTerminator here] -- ContinueStatement : continue ; continue [no LineTerminator here] LabelIdentifier ; BreakStatement : break ; break [no LineTerminator here] LabelIdentifier ; ReturnStatement : return ; return [no LineTerminator here] Expression ; ThrowStatement : throw [no LineTerminator here] Expression ; ArrowFunction : ArrowParameters [no LineTerminator here] => ConciseBody YieldExpression : yield [no LineTerminator here] * AssignmentExpression yield [no LineTerminator here] AssignmentExpression
经典的例子,与ReturnStatement
:
return "something"; // is transformed to return; "something";
直接从ECMA-262,第五版ECMAScript规范 :
7.9.1自动分号插入规则
有三个分号插入的基本规则:
- 当从左向右parsing程序时,遇到任何语法产生都不允许的令牌(称为违规令牌 ),那么在冒犯令牌之前自动插入分号,如果一个或多个以下条件是正确的:
- 有问题的令牌至less由一个
LineTerminator
与前一个令牌分开。- 有罪的令牌是} 。
- 当从左到右分析程序时,会遇到令牌inputstream的结束,并且parsing器无法将input令牌streamparsing为单个完整的ECMAScript
Program
,则会在分配结束时自动插入分号inputstream。- 当程序从左到右被parsing时,会遇到某个语法生成所允许的令牌,但是生产是一个受限制的生产,并且该令牌将是紧跟在注释之后的terminal或非terminal的第一个令牌在受限制的生产中(因此这样的一个标记被称为受限制的标记)中的“ [这里没有
LineTerminator
] ”,并且受限制的标记与前一个标记由至less一个LineTerminator分开,然后在受限制的标记之前自动插入一个分号。但是,对于上述规则,还有一个额外的重载条件:如果分号会被分析为空语句,或者如果该分号将成为for语句头部中的两个分号之一,则从不自动插入分号(请参见12.6.3)。
我无法很好地理解规范中的这三条规则 – 希望能有一些更简单的英文 – 但是这里是我从JavaScript中收集的:“权威指南”,第6版,David Flanagan,O'Reilly,2011:
引用:
JavaScript不会将每一行换行符当作分号:只有在分号不能分析代码时,它通常将换行符当作分号。
另外引用:代码
var a a = 3 console.log(a)
JavaScript不会将第二个换行符作为分号处理,因为它可以继续parsing较长的语句a = 3;
和:
两个例外的情况是,当JavaScript解释行分号时,它不能将第二行parsing为第一行语句的延续。 第一个exception涉及返回,中断和继续语句
…如果在这些单词之后出现换行符… JavaScript将始终将该换行符解释为分号。
…第二个exception涉及++和 – 操作符…如果要将这两个操作符中的任何一个用作后缀运算符,则它们必须与它们所应用的expression式出现在同一行上。 否则,换行符将被视为分号,++或 – 将被parsing为应用于后面代码的前缀运算符。 考虑这个代码,例如:
x ++ y
它被parsing为
x; ++y;
x; ++y;
,而不是x++; y
x++; y
所以我想简化它,这意味着:
一般来说,只要有意义,JavaScript就会把它当作代码的延续 – 除了两种情况:(1)在一些关键字如return
, break
, continue
,(2)如果它看到++
或--
新的一行,那么它会加上;
在前一行的结尾。
关于“只要有意义就把它当作代码的延续”的部分,就像正则expression式的贪婪匹配一样。
如上所述,这意味着用换行符return
,JavaScript解释器将插入一个;
(再次引用:如果在这些单词之后出现换行符(例如return
)… JavaScript将始终将该换行符解释为分号)
并由于这个原因,经典的例子
return { foo: 1 }
将无法按预期工作,因为JavaScript解释器会将其视为:
return; // returning nothing { foo: 1 }
return
后不得立即return
:
return { foo: 1 }
为它正常工作。 你可以插入一个;
你自己如果遵循使用a的规则;
在任何陈述之后:
return { foo: 1 };
关于分号插入和var语句,当使用var但是跨越多行时,请注意忘记逗号。 有人昨天在我的代码中发现了这个:
var srcRecords = src.records srcIds = [];
它运行的结果是,srcIds声明/赋值是全局的,因为由于自动分号插入的原因,上一行var的本地声明不再被应用,因为该声明被认为是完成的。