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


445

好吧,首先我应该问一下这是否与浏览器有关。

我已经读到,如果找到了无效的令牌,但是代码段在该无效令牌之前一直有效,如果在该令牌之前加了换行符,则在该令牌之前插入一个分号。

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

return
  _a+b;

..似乎不遵循此规则,因为_a是有效的令牌。

另一方面,分解呼叫链可以按预期工作:

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

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


22
这里一个规范 ...
万里


3
参见第 以上引用的PDF中的26。
ᴠɪɴᴄᴇɴᴛ


请参阅第11.9节“自动分号插入”
Lam Andrew

Answers:


454

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

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

ASI的具体规则在规范第11.9.1节“自动分号插入规则”中进行了描述

描述了三种情况:

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

    • 令牌与上一个令牌分隔至少一个LineTerminator
    • 令牌是 }

    例如

    { 1
    2 } 3

    转化为

    { 1
    ;2 ;} 3;

    NumericLiteral 1满足所述第一条件,令牌是行终止如下。
    2满足第二条件,令牌是以下}

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

    例如

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

4
#1:语法不允许的标记通常不是行终止符,是这样吗(除非您是指#3的受限产生式)?它认为您应该省略括号。#2 ++c为了清晰起见,示例不应该仅显示插入后的内容吗?
Bergi 2014年

2
请注意,ASI不需要实际上“插入分号”,只需在引擎的解析器中终止该语句即可...
Aprillion

1
它说的是“输入流”,这意味着“一行”吗?“输入令牌流”使它变得有点难以理解
极性

规格链接是否对其他人有用?它导致我进入一个几乎空白的页面,上面有一个死链接。
intcreator

请解释根据这些规则,太极者无极而生的“ a [LineBreak] = [LineBreak] 3”下面的示例如何工作
NirO。Sir21

45

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

7.9.1自动分号插入规则

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

  1. 当(从左至右)解析程序时,遇到任何语法生成都不允许的令牌(称为冒犯令牌)时,如果以下一项或多项操作,则在冒犯令牌之前会自动插入分号条件为真:
    • 有问题的令牌与先前的令牌之间至少相隔一个LineTerminator
    • 令人讨厌的令牌是}
  2. 当从左向右解析程序时,遇到令牌输入流的末尾并且解析器无法将输入令牌流解析为单个完整的ECMAScript Program,则分号将自动插入到末尾。输入流。
  3. 当从左到右解析程序时,遇到某种语法的某种生产所允许的标记,但是生产是受限制的生产,并且该标记将成为紧随注解之后的终端或非终端的第一个标记在受限生产中使用“ [no LineTerminatorhere] ”(因此这种令牌称为受限令牌),并且受限令牌通过至少一个LineTerminator与前一个令牌分开,然后在受限令牌之前自动插入分号。

但是,上述规则还有一个附加的覆盖条件:如果分号随后将被解析为空语句,或者该分号成为for语句标题中的两个分号之一,则永远不会自动插入分号(请参见12.6)。 .3)。


44

我无法很好地理解规范中的这3条规则-希望能用更简单的英语来表达-但是这是我从JavaScript中收集的:《权威指南》,第6版,戴维·弗拉纳根(David Flanagan),O'Reilly,2011年:

引用:

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

另一个报价:对于代码

var a
a
=
3 console.log(a)

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

和:

JavaScript不能将第二行解释为第一行语句的延续时,将换行符解释为分号的一般规则有两个例外。第一个异常涉及return,break和Continue语句

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

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

x 
++ 
y

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

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

在一般情况下,JavaScript的把它当作码延续,只要它是有道理的-除了两种情况:(1)经过一些关键字时returnbreakcontinue,和(2)如果看到++或者--在新的一行,则反而会加重该;在前一行的末尾。

关于“只要有意义就将其视为代码的延续来进行处理”的部分使人感到正则表达式的贪婪匹配。

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

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

由于这个原因,

return
{ 
  foo: 1
}

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

return;   // returning nothing
{
  foo: 1
}

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

return { 
  foo: 1
}

使其正常工作。;如果要遵循;在任何语句后使用a的规则,则可以插入自己:

return { 
  foo: 1
};

17

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

    var srcRecords = src.records
        srcIds = [];

它运行了,但结果是srcIds声明/赋值是全局的,因为由于自动分号插入导致该语句被视为完成,因此不再应用上一行的带有var的本地声明。


4
这有点是为什么我使用jsLint的原因
Zach Lysobey 2013年

1
JsHint / Lint直接在您的代码编辑器中立即响应:)
dmi3y 2013年

5
@balupton当忘记了本行的逗号时,会自动插入一个分号。与规则相反,它更像是“陷阱”。
Dexygen

1
我认为balupton是正确的,如果您这样写,那就是不同的:var srcRecords = src.records srcIds = [];在一行中忘了逗号,或者您写“ return a && b”却什么也没有...但是在a之前的换行符会在返回后插入一个自动分号,这是由ASI规则定义的…
塞巴斯蒂安

3
我认为打字的清晰度varletconst每行胜过第二它需要键入它的级分)。
squidbe '18

5

我发现对JavaScript的自动分号插入的最上下文的描述来自一本有关制作解释器的书。

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

他继续描述它,就像您会编码气味一样

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.