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

家住魔仙堡 提交于 2020-02-27 06:52:59

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

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

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

return
  _a+b;

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

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

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

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


#1楼

首先,您应该知道哪些语句受自动分号插入(为简洁起见也称为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";

#2楼

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

7.9.1自动分号插入规则

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

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

但是,上述规则还有一个额外的优先条件:如果分号随后将被解析为空语句,或者该分号成为for语句的标头中的两个分号之一,则永远不会自动插入分号。 12.6.3)。


#3楼

我无法很好地理解规范中的这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; ,而不是x++; y x++; y

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

一般而言,JavaScript会在可能的范围内将其视为代码的延续-两种情况除外:(1)在某些关键字(如returnbreakcontinue和(2)在看到++--之后)新行,然后将添加; 在上一行的末尾。

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

有了以上所述,这意味着return行并return ,JavaScript解释器将插入;

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

由于这个原因,

return
{ 
  foo: 1
}

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

return;   // returning nothing
{
  foo: 1
}

return后必须没有换行符:

return { 
  foo: 1
}

使其正常工作。 并且您可以插入; 如果您要遵循使用a的规则; 在任何声明之后:

return { 
  foo: 1
};

#4楼

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

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

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

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


#5楼

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

    var srcRecords = src.records
        srcIds = [];

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!