好吧,首先我可能应该问这是否取决于浏览器。
我已经读过,如果找到了一个无效的标记,但代码部分在该无效标记之前是有效的,如果它前面有一个换行符,则在该标记之前插入一个分号。
但是,由分号插入引起的错误引用的常见示例是:
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 版,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++; y
所以我认为简化它,这意味着:
一般来说,只要有意义,JavaScript 就会将其视为代码的延续——除了 2 种情况:(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 是我所知道的唯一一种语言,许多风格指南要求在每条语句后使用明确的分号,尽管理论上该语言允许您省略它们。
只是补充一下,
const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
console.log("aa");
})()
看到这个,使用立即调用的函数表达式(IIFE)