JavaScript的自动分号插入(ASI)有哪些规则?

T.R*_*.R. 400 javascript automatic-semicolon-insertion

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

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

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

return
  _a+b;
Run Code Online (Sandbox Code Playgroud)

..它似乎不遵循这个规则,因为_a将是一个有效的标记.

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

$('#myButton')
  .click(function(){alert("Hello!")});
Run Code Online (Sandbox Code Playgroud)

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

CMS*_*CMS 420

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

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

ASI的具体规则在规范§11.9.1自动分号插入规则中有所描述

描述了三种情况:

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

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

    例如:

    { 1
    2 } 3
    
    Run Code Online (Sandbox Code Playgroud)

    变成了

    { 1
    ;2 ;} 3;
    
    Run Code Online (Sandbox Code Playgroud)

    NumericLiteral 1满足所述第一条件,下面令牌是一个行结束.
    2满足第二条件,下面令牌}.

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

    例如:

    a = b
    ++c
    
    Run Code Online (Sandbox Code Playgroud)

    转变为:

    a = b;
    ++c;
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)

    经典的例子,有ReturnStatement:

    return 
      "something";
    
    Run Code Online (Sandbox Code Playgroud)

    变成了

    return;
      "something";
    
    Run Code Online (Sandbox Code Playgroud)

  • #1:语法不允许的令牌通常不是行终止符,是不是(除非你的意思是来自#3的限制制作)?它认为你应该省略括号.#2为了清楚起见,这个例子不应该只显示`++ c`之后的插入吗? (3认同)
  • 请注意ASI不需要实际"插入分号",只是为了终止引擎解析器中的语句... (2认同)

Jör*_*tag 41

直接来自ECMA-262,第五版ECMAScript规范:

7.9.1自动分号插入规则

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

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

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


nop*_*ole 35

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

引用:

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

另一个引用:代码

var a
a
=
3 console.log(a)
Run Code Online (Sandbox Code Playgroud)

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

和:

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

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

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

x 
++ 
y
Run Code Online (Sandbox Code Playgroud)

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

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

在一般情况下,用JavaScript,只要它是有意义把它当作代码延续-除了两种情况:(1)像一些关键字后return,break,continue,和(2)如果看到++或者--在新的一行,则反而会加重该;在前一行的末尾.

关于"将其视为代码的延续,只要它有意义"的部分使得它感觉像正则表达式的贪婪匹配.

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

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

并且由于这个原因,经典的例子

return
{ 
  foo: 1
}
Run Code Online (Sandbox Code Playgroud)

将无法正常工作,因为JavaScript解释器会将其视为:

return;   // returning nothing
{
  foo: 1
}
Run Code Online (Sandbox Code Playgroud)

return:之后必须立即没有换行符:

return { 
  foo: 1
}
Run Code Online (Sandbox Code Playgroud)

为了它正常工作.;如果您遵循使用;后任何声明的规则,您可以插入自己:

return { 
  foo: 1
};
Run Code Online (Sandbox Code Playgroud)


Geo*_*pty 17

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

    var srcRecords = src.records
        srcIds = [];
Run Code Online (Sandbox Code Playgroud)

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

  • @balupton当忘记结束该行的逗号时,会自动插入一个分号.与规则相反,它更像是"陷阱". (5认同)
  • 这就是我使用jsLint的原因 (4认同)
  • 这也可以通过使用严格模式来捕获,作为对未声明变量的赋值。 (2认同)
  • 我认为在每行上键入`var`(`let`,`const`)的清晰度超过键入它所花费的时间的几分之一。 (2认同)

jch*_*ook 8

我发现的 JavaScript自动分号插入的最上下文描述来自一本关于Crafting Interpreters的书。

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

他继续将其描述为代码气味

如果我详细说明它是如何工作的,那么这个设计说明就会变成设计谩骂,更不用说这是一个坏主意的所有各种方式。一团糟。JavaScript 是我所知道的唯一一种语言,其中许多样式指南要求在每个语句后显式分号,即使该语言理论上允许您省略它们。