好吧,首先我可能应该问这是否取决于浏览器。
我已经读过,如果找到了一个无效的标记,但代码部分在该无效标记之前是有效的,如果它前面有一个换行符,则在该标记之前插入一个分号。
但是,由分号插入引起的错误引用的常见示例是:
return
_a+b;
..这似乎不遵循这个规则,因为 _a 将是一个有效的令牌。
另一方面,分解调用链按预期工作:
$('#myButton')
.click(function(){alert("Hello!")});
有没有人对规则有更深入的描述?
首先你应该知道哪些语句会受到自动分号插入的影响(也称为 ASI):
空语句
var 语句
表达式语句
do-while 语句
继续声明
中断语句
退货声明
抛出语句
ASI 的具体规则,在规范 §11.9.1 Rules of Automatic Semicolon Insertion 中进行了描述
描述了三种情况:
当遇到语法不允许的违规标记时,如果出现以下情况,则会在其前面插入分号:
该标记与前一个标记至少有一个 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 版,David Flanagan,O'Reilly,2011:
引用:
JavaScript 不会将每个换行符都视为分号:它通常仅在无法解析没有分号的代码时才将换行符视为分号。
另一个引用:对于代码
var a
a
=
3 console.log(a)
JavaScript 不会将第二个换行符视为分号,因为它可以继续解析更长的语句 a = 3;
和:
当 JavaScript 无法将第二行解析为第一行语句的延续时,JavaScript 将换行符解释为分号的一般规则有两个例外。第一个例外涉及 return、break 和 continue 语句……如果换行符出现在这些单词中的任何一个之后……JavaScript 将始终将该换行符解释为分号。 ...第二个例外涉及 ++ 和 −− 运算符 ...如果您想将这些运算符中的任何一个用作后缀运算符,它们必须与它们所应用的表达式出现在同一行。否则,换行符将被视为分号,而 ++ 或 -- 将被解析为应用于后面代码的前缀运算符。考虑这段代码,例如:
x
++
y
它被解析为 x; ++y;,不像 x++;是的
所以我认为简化它,这意味着:
一般来说,只要有意义,JavaScript 就会将其视为代码的延续——除了 2 种情况:(1) 在 return
、break
、continue
等关键字之后,以及 (2) if它会在新行上看到 ++
或 --
,然后将 ;
添加到上一行的末尾。
“只要有意义就将其视为代码的延续”的部分让人感觉像是正则表达式的贪婪匹配。
如上所述,这意味着对于带有换行符的 return
,JavaScript 解释器将插入一个 ;
(再次引用:如果在这些词中的任何一个之后出现换行符 [例如 return
] ... JavaScript 将始终将该换行符解释为分号)
由于这个原因,经典的例子
return
{
foo: 1
}
将无法按预期工作,因为 JavaScript 解释器会将其视为:
return; // returning nothing
{
foo: 1
}
return
之后必须没有换行符:
return {
foo: 1
}
使其正常工作。如果您要遵循在任何语句后使用 ;
的规则,您可以自己插入 ;
:
return {
foo: 1
};
直接来自 ECMA-262, Fifth Edition ECMAScript Specification:
7.9.1 自动分号插入规则 分号插入有三个基本规则: 当程序从左到右解析时,遇到任何语法生成都不允许的标记(称为违规标记)时,如果以下一个或多个条件为真,则在违规记号之前自动插入分号: 违规记号与前一个记号由至少一个 LineTerminator 隔开。有问题的令牌是 }。当程序从左到右解析时,遇到输入标记流的结尾,并且解析器无法将输入标记流解析为单个完整的 ECMAScript 程序,则在结尾处自动插入分号输入流。当程序从左到右解析时,遇到语法的某些产生式允许的令牌,但产生式是受限产生式,并且该令牌将是紧接在注释之后的终端或非终端的第一个令牌“[no LineTerminator here]”在受限产生式中(因此这样的记号被称为受限记号),并且受限记号与前一个记号至少有一个 LineTerminator 分隔,然后在受限记号前自动插入分号.但是,上述规则还有一个额外的覆盖条件:如果分号随后将被解析为空语句,或者该分号将成为 for 语句标题中的两个分号之一,则永远不会自动插入分号(请参阅12.6.3)。
关于分号插入和 var 语句,请注意在使用 var 但跨越多行时忘记逗号。昨天有人在我的代码中发现了这个:
var srcRecords = src.records
srcIds = [];
它运行了,但效果是 srcIds 声明/赋值是全局的,因为前一行带有 var 的本地声明不再适用,因为由于自动分号插入,该语句被认为已完成。
var srcRecords = src.records srcIds = [];
并忘记逗号,或者您写“返回 a && b”并且什么都不忘...但是 a 之前的换行符会插入返回后的自动分号,由 ASI 规则定义...
var
(let
, const
) 的清晰度超过了输入它所需的几分之一秒。
我发现的关于 JavaScript Automatic Semicolon Insertion 的最符合上下文的描述来自一本关于 Crafting Interpreters 的书。
JavaScript 的“自动分号插入”规则很奇怪。在其他语言假设大多数换行符是有意义的并且在多行语句中只应忽略少数的情况下,JS 假设相反。除非遇到解析错误,否则它将所有换行符视为无意义的空格。如果是这样,它会返回并尝试将前一个换行符转换为分号以获得语法上有效的内容。
他继续描述它就像你会code smell。
如果我详细介绍它的工作原理,更不用说这是一个坏主意的各种方式,那么这个设计说明就会变成一个设计诽谤。一团糟。 JavaScript 是我所知道的唯一一种语言,许多风格指南要求在每条语句后使用明确的分号,尽管理论上该语言允许您省略它们。
只是补充一下,
const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
console.log("aa");
})()
看到这个,using immediately invoked function expression(IIFE)
++c
之后的插入吗?