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自动插入分号的规则

描述了三种情况:

  1. 遇到语法不允许的令牌( LineTerminator} )时,如果出现以下情况,则在其之前插入分号:

    • 令牌与前一个令牌至less由一个LineTerminator分开。
    • 令牌是}

    例如:

      { 1 2 } 3 // is transformed to { 1 ;2 ;} 3; 

    NumericLiteral 1符合第一个条件,以下标记是行终止符。 2满足第二个条件,下面的标记是}

  2. 当遇到令牌的inputstream的结束并且parsing器不能将input令牌streamparsing为单个完整的程序时,则在inputstream的末尾自动插入分号。

    例如:

     a = b ++c // is transformed to: a = b; ++c; 
  3. 这种情况发生在语法的某些生成允许令牌的情况下,但是生产是限制生产 ,在限制令牌之前自动插入分号。

限制作品:

 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自动分号插入规则

有三个分号插入的基本规则:

  1. 当从左向右parsing程序时,遇到任何语法产生都不允许的令牌(称为违规令牌 ),那么在冒犯令牌之前自动插入分号,如果一个或多个以下条件是正确的:
    • 有问题的令牌至less由一个LineTerminator与前一个令牌分开。
    • 有罪的令牌是}
  2. 当从左到右分析程序时,会遇到令牌inputstream的结束,并且parsing器无法将input令牌streamparsing为单个完整的ECMAScript Program ,则会在分配结束时自动插入分号inputstream。
  3. 当程序从左到右被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)在一些关键字如returnbreakcontinue ,(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的本地声明不再被应用,因为该声明被认为是完成的。