好吧,首先我应该问这是否与浏览器有关。
我已经读过,如果找到一个无效的标记,但该部分代码在该无效标记之前一直有效,如果它前面有一个换行符,则会在该标记之前插入一个分号。
但是,引用的由分号插入引起的错误的常见示例是:
return
_a+b;
..这似乎不遵循此规则,因为 _a 将是一个有效的标记。
另一方面,分解调用链按预期工作:
$('#myButton')
.click(function(){alert("Hello!")});
有没有人对规则有更深入的描述?
好吧,首先我应该问这是否与浏览器有关。
我已经读过,如果找到一个无效的标记,但该部分代码在该无效标记之前一直有效,如果它前面有一个换行符,则会在该标记之前插入一个分号。
但是,引用的由分号插入引起的错误的常见示例是:
return
_a+b;
..这似乎不遵循此规则,因为 _a 将是一个有效的标记。
另一方面,分解调用链按预期工作:
$('#myButton')
.click(function(){alert("Hello!")});
有没有人对规则有更深入的描述?
首先你应该知道哪些语句会受到自动分号插入的影响(也称为 ASI 为简洁起见):
var 陈述do-while 陈述continue 陈述break 陈述return 陈述throw 陈述ASI的具体规则,在规范§11.9.1自动分号插入规则中描述
描述了三种情况:
LineTerminator。}例如:
{ 1
2 } 3
转化为
{ 1
;2 ;} 3;
在NumericLiteral 1满足所述第一条件,令牌是行终止如下。
在2满足第二条件,令牌是以下}。
例如:
a = b
++c
转化为:
a = b;
++c;
限制生产:
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";
我不太理解规范中的这 3 条规则——希望有一些更简单的英语——但这是我从 JavaScript 收集的:权威指南,第 6 版,大卫弗拉纳根,奥莱利,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)经过一些关键字时return,break,continue,和(2)如果看到++或者--在新的一行,则反而会加重该;在前一行的末尾。
关于“只要有意义就将其视为代码的延续”的部分让人感觉像是正则表达式的贪婪匹配。
如上所述,这意味着对于return换行符,JavaScript 解释器将插入一个;
(再次引用:如果在这些单词中的任何一个之后出现换行符 [例如return] ... JavaScript 将始终将该换行符解释为分号)
由于这个原因,经典的例子
return
{
foo: 1
}
不会按预期工作,因为 JavaScript 解释器会将其视为:
return; // returning nothing
{
foo: 1
}
在 之后必须没有换行符return:
return {
foo: 1
}
使其正常工作。;如果您要遵循;在任何语句之后使用 a 的规则,您可以自己插入 a :
return {
foo: 1
};
直接来自ECMA-262,第五版 ECMAScript 规范:
7.9.1 自动分号插入规则
分号插入的三个基本规则:
- 当程序从左到右解析时,遇到任何语法产生式都不允许的记号(称为违规记号),如果出现以下一项或多项情况,则会在违规记号前自动插入分号条件为真:
- 违规令牌与前一个令牌至少隔开一个
LineTerminator。- 违规令牌是}。
- 当程序从左到右解析时,遇到令牌输入流的末尾并且解析器无法将输入令牌流解析为单个完整的 ECMAScript 时
Program,则自动在末尾插入分号输入流。- 当程序从左到右解析时,遇到语法的某些产生式允许的标记,但该产生式是受限产生式并且该标记将是紧跟注释之后的终结符或非终结符的第一个标记“ [no
LineTerminatorhere] ”在受限产生式中(因此这种标记被称为受限标记),并且受限标记与前一个标记至少被一个LineTerminator分隔,然后在受限标记之前自动插入一个分号。但是,前面的规则还有一个额外的覆盖条件:如果分号随后会被解析为空语句,或者该分号将成为for语句标题中的两个分号之一,则永远不会自动插入分号(参见 12.6 .3)。
关于分号插入和 var 语句,请注意在使用 var 但跨越多行时忘记逗号。昨天有人在我的代码中发现了这个:
var srcRecords = src.records
srcIds = [];
它运行了,但结果是 srcIds 声明/赋值是全局的,因为前一行带有 var 的本地声明不再适用,因为由于自动分号插入,该语句被认为已完成。
我发现的 JavaScript自动分号插入的最上下文描述来自一本关于Crafting Interpreters的书。
JavaScript 的“自动分号插入”规则很奇怪。在其他语言假设大多数换行符是有意义的并且在多行语句中只有少数应该被忽略的地方,JS 假设相反。除非遇到解析错误,否则它将所有换行符视为无意义的空格。如果是,它会返回并尝试将前一个换行符转换为分号以获得语法上有效的内容。
他继续将其描述为代码气味。
如果我深入了解它是如何工作的完整细节,那么这个设计说明将变成设计谩骂,更不用说这是一个坏主意的所有各种方式。一团糟。JavaScript 是我所知道的唯一一种语言,其中许多样式指南要求在每个语句后显式分号,即使该语言理论上允许您省略它们。