JavaScript自动分号插入(ASI)的规则是什么?

时间:2010-05-17 02:06:22

标签: javascript automatic-semicolon-insertion

好吧,首先我应该问一下这是否依赖于浏览器。

我已经读过,如果找到了无效的令牌,但代码段在该无效令牌之前有效,则在令牌之前插入分号(如果前面有换行符。)

但是,由分号插入引起的错误引用的常见示例是:

return
  _a+b;

..似乎没有遵循这条规则,因为_a将是一个有效的令牌。

另一方面,拆分调用链按预期工作:

$('#myButton')
  .click(function(){alert("Hello!")});

有没有人对这些规则有更深入的描述?

6 个答案:

答案 0 :(得分:388)

首先,您应该知道哪些语句受自动分号插入影响(为简洁起见,也称为ASI):

  • 空声明
  • var声明
  • 表达陈述
  • do-while声明
  • continue声明
  • break声明
  • return声明
  • throw声明

ASI的具体规则在规范§11.9.1 Rules of Automatic Semicolon Insertion

中描述

描述了三个案例:

  1. 当遇到语法不允许的标记(LineTerminator})时,如果出现分号,则会在其前插入分号:

    • 令牌与前一个令牌分开至少一个LineTerminator
    • 令牌为}

    e.g

    { 1
    2 } 3
    

    转换为

    { 1
    ;2 ;} 3;
    

    NumericLiteral 1符合第一个条件,以下标记是行终止符 2符合第二个条件,以下标记为}

  2. 当遇到令牌输入流的末尾且解析器无法将输入令牌流解析为单个完整的程序时,则会在输入流的末尾自动插入分号。

    e.g

    a = b
    ++c
    

    转换为:

    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";
    

    转换为

    return;
      "something";
    

答案 1 :(得分:40)

直接来自ECMA-262, Fifth Edition ECMAScript Specification

  

7.9.1自动分号插入规则

     

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

     
      
  1. 当从左到右解析程序时,会遇到任何语法生成不允许的令牌(称为违规令牌),然后会自动插入分号如果满足以下一个或多个条件,则为违规令牌:      
        
    • 违规令牌与前一令牌分开至少一个LineTerminator
    •   
    • 违规令牌是}
    •   
  2.   
  3. 当从左到右解析程序时,会遇到令牌输入流的末尾,并且解析器无法将输入令牌流解析为单个完整的ECMAScript Program,然后是分号将自动插入输入流的末尾。
  4.   
  5. 当从左到右解析程序时,会遇到某些语法生成所允许的令牌,但生产是限制生产,令牌将是第一个紧跟在限制生产中注释“ [no LineTerminator here ”之后的终端或非终端的令牌(因此这样的令牌称为受限令牌),受限令牌是通过至少一个 LineTerminator 与前一个标记分开,然后在限制标记之前自动插入分号。
  6.         

    但是,前面的规则还有一个额外的重要条件:如果分号将被解析为空语句,或者如果该分号将成为<的标题中的两个分号之一,则永远不会自动插入分号kbd> for 语句(见12.6.3)。

答案 2 :(得分:33)

我无法理解规范中的这3条规则 - 希望能有更简单的英语 - 但这是我从JavaScript中收集到的:The Definitive Guide,第6版,David Flanagan,O&#39 ; Reilly,2011:

引用:

  

JavaScript不会将每个换行符视为分号:只有在没有分号的情况下无法解析代码时,它通常会将换行符视为分号。

另一个引用:代码

var a
a
=
3 console.log(a)
  

JavaScript不会将第二个换行符视为分号,因为它可以继续解析较长的语句a = 3;

  

一般规则的两个例外,当JavaScript无法将第二行解析为第一行语句的延续时,JavaScript将换行符解释为分号。第一个例外涉及return,break和continue语句

     

...如果在任何这些单词之后出现换行符...... JavaScript将始终将该换行符解释为分号。

     

......第二个例外涉及++和 - 运算符......如果要将这些运算符中的任何一个用作后缀运算符,它们必须与它们应用的表达式出现在同一行。否则,换行符将被视为分号,++或 - 将被解析为应用于后面的代码的前缀运算符。请考虑此代码,例如:

x 
++ 
y
  

它被解析为x; ++y;,而不是x++; y

所以我想简化它,这意味着:

一般情况下,只要有意义,JavaScript会将其视为代码的延续 - 除了2种情况:(1)在某些关键字之后,例如returnbreak,{{ 1}},以及(2)如果它在新行上看到continue++,则会在上一行的末尾添加-- < / p>

关于&#34;将其视为代码的延续,只要它有意义&#34;让它感觉像是正则表达式的贪婪匹配。

如上所述,这意味着对于;换行符,JavaScript解释器将插入return

(再次引用:如果在任何这些单词之后出现换行符[例如;] ...... JavaScript将始终将该换行符解释为分号)

由于这个原因,

的经典例子
return

将无法按预期工作,因为JavaScript解释器会将其视为:

return
{ 
  foo: 1
}

return; // returning nothing { foo: 1 }

后必须立即没有换行符
return

让它正常工作。如果您在任何声明后遵循使用return { foo: 1 } 的规则,您可以自己插入;

;

答案 3 :(得分:17)

关于分号插入和var语句,请注意在使用var但跨越多行时忘记逗号。有人昨天在我的代码中发现了这个:

    var srcRecords = src.records
        srcIds = [];

它运行但效果是srcIds声明/赋值是全局的,因为由于自动分号插入,因为该语句被认为已完成,因此前一行中的var的本地声明不再适用。

答案 4 :(得分:1)

我发现对JavaScript Automatic Semicolon Insertion的最上下文描述来自一本关于Crafting Interpreters的书。

  

JavaScript的“自动分号插入”规则很奇怪。在其他语言假设大多数换行符有意义的情况下,而在多行语句中仅应忽略少数几种,JS则相反。除非遇到解析错误,否则它将所有换行符视为无意义的空格。如果是这样,它将返回并尝试将前一个换行符转换为分号以获取语法上有效的内容。

他继续像您code smell那样描述它。

  

如果我详细介绍它的工作原理,那么本设计说明将变成一个设计思路,更不用说是一个坏主意的所有各种方式了。一团糟。我知道JavaScript是唯一的一种语言,尽管从理论上讲它可以让您忽略它们,但许多样式指南在每个语句后都需要显式分号。

答案 5 :(得分:0)

只需添加

const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
    console.log("aa");
})()

请参见using immediately invoked function expression(IIFE)