JavaScript 自动分号插入 (ASI) 的规则是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2846283/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are the rules for JavaScript's automatic semicolon insertion (ASI)?
提问by T.R.
Well, first I should probably ask if this is browser dependent.
好吧,首先我应该问这是否依赖于浏览器。
I've read that if an invalid token is found, but the section of code is valid until that invalid token, a semicolon is inserted before the token if it is preceded by a line break.
我已经读过,如果找到一个无效的标记,但该部分代码在该无效标记之前一直有效,如果它前面有一个换行符,则会在该标记之前插入一个分号。
However, the common example cited for bugs caused by semicolon insertion is:
但是,引用的由分号插入引起的错误的常见示例是:
return
_a+b;
..which doesn't seem to follow this rule, since _a would be a valid token.
..这似乎不遵循此规则,因为 _a 将是一个有效的标记。
On the other hand, breaking up call chains works as expected:
另一方面,分解调用链按预期工作:
$('#myButton')
.click(function(){alert("Hello!")});
Does anyone have a more in-depth description of the rules?
有人对规则有更深入的描述吗?
回答by CMS
First of all you should know which statements are affected by the automatic semicolon insertion (also known as ASI for brevity):
首先你应该知道哪些语句会受到自动分号插入的影响(也称为 ASI 为简洁起见):
- empty statement
varstatement- expression statement
do-whilestatementcontinuestatementbreakstatementreturnstatementthrowstatement
- 空语句
var陈述- 表达式语句
do-while陈述continue陈述break陈述return陈述throw陈述
The concrete rules of ASI, are described in the specification §11.9.1 Rules of Automatic Semicolon Insertion
ASI的具体规则,在规范§11.9.1自动分号插入规则中描述
Three cases are described:
描述了三种情况:
When a token (
LineTerminatoror}) is encountered that is not allowed by the grammar, a semicolon is inserted before it if:- The token is separated from the previous token by at least one
LineTerminator. - The token is
}
e.g.:
{ 1 2 } 3is transformed to
{ 1 ;2 ;} 3;The
NumericLiteral1meets the first condition, the following token is a line terminator.
The2meets the second condition, the following token is}.- The token is separated from the previous token by at least one
When the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete Program, then a semicolon is automatically inserted at the end of the input stream.
e.g.:
a = b ++cis transformed to:
a = b; ++c;This case occurs when a token is allowed by some production of the grammar, but the production is a restricted production, a semicolon is automatically inserted before the restricted token.
Restricted productions:
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] AssignmentExpressionThe classic example, with the
ReturnStatement:return "something";is transformed to
return; "something";
当遇到语法不允许的记号 (
LineTerminatoror}) 时,在以下情况下会在其前插入分号:- 该令牌与前一个令牌至少隔开一个
LineTerminator。 - 令牌是
}
例如:
{ 1 2 } 3转化为
{ 1 ;2 ;} 3;在
NumericLiteral1满足所述第一条件,令牌是行终止如下。
在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";
回答by J?rg W Mittag
Straight from the ECMA-262, Fifth Edition ECMAScript Specification:
直接来自ECMA-262,第五版 ECMAScript 规范:
7.9.1 Rules of Automatic Semicolon Insertion
There are three basic rules of semicolon insertion:
- When, as the program is parsed from left to right, a token (called the offending token) is encountered that is not allowed by any production of the grammar, then a semicolon is automatically inserted before the offending token if one or more of the following conditions is true:
- The offending token is separated from the previous token by at least one
LineTerminator.- The offending token is }.
- When, as the program is parsed from left to right, the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete ECMAScript
Program, then a semicolon is automatically inserted at the end of the input stream.- When, as the program is parsed from left to right, a token is encountered that is allowed by some production of the grammar, but the production is a restricted productionand the token would be the first token for a terminal or nonterminal immediately following the annotation "[no
LineTerminatorhere]" within the restricted production (and therefore such a token is called a restricted token), and the restricted token is separated from the previous token by at least one LineTerminator, then a semicolon is automatically inserted before the restricted token.However, there is an additional overriding condition on the preceding rules: a semicolon is never inserted automatically if the semicolon would then be parsed as an empty statement or if that semicolon would become one of the two semicolons in the header of a forstatement (see 12.6.3).
7.9.1 自动分号插入规则
分号插入的三个基本规则:
- 当程序从左到右解析时,遇到任何语法产生式都不允许的记号(称为违规记号),如果出现以下一项或多项情况,则会在违规记号前自动插入分号条件为真:
- 违规令牌与前一个令牌至少隔开一个
LineTerminator。- 违规令牌是}。
- 当程序从左到右解析时,遇到令牌输入流的末尾并且解析器无法将输入令牌流解析为单个完整的 ECMAScript 时
Program,则自动在末尾插入分号输入流。- 当程序从左到右解析时,遇到语法的某些产生式允许的标记,但该产生式是受限产生式并且该标记将是紧跟注释之后的终结符或非终结符的第一个标记“ [no
LineTerminatorhere]”在受限产生式中(因此这种标记被称为受限标记),并且受限标记与前一个标记至少被一个LineTerminator分隔,然后在受限标记之前自动插入一个分号。但是,前面的规则有一个额外的覆盖条件:如果分号随后被解析为空语句,或者该分号将成为for语句标题中的两个分号之一,则永远不会自动插入分号(参见 12.6 .3)。
回答by nonopolarity
I could not understand those 3 rules in the specs too well -- hope to have something that is more plain English -- but here is what I gathered from JavaScript: The Definitive Guide, 6th Edition, David Flanagan, O'Reilly, 2011:
我不太理解规范中的这 3 条规则——希望有一些更简单的英语——但这是我从 JavaScript 收集的:权威指南,第 6 版,大卫弗拉纳根,奥莱利,2011 年:
Quote:
引用:
JavaScript does not treat every line break as a semicolon: it usually treats line breaks as semicolons only if it can't parse the code without the semicolons.
JavaScript 不会将每个换行符都视为分号:它通常仅在无法解析没有分号的代码时才将换行符视为分号。
Another quote: for the code
另一个引用:对于代码
var a
a
=
3 console.log(a)
JavaScript does not treat the second line break as a semicolon because it can continue parsing the longer statement a = 3;
JavaScript 不会将第二个换行符视为分号,因为它可以继续解析更长的语句 a = 3;
and:
和:
two exceptions to the general rule that JavaScript interprets line breaks as semicolons when it cannot parse the second line as a continuation of the statement on the first line. The first exception involves the return, break, and continue statements
... If a line break appears after any of these words ... JavaScript will always interpret that line break as a semicolon.
... The second exception involves the ++ and ?? operators ... If you want to use either of these operators as postfix operators, they must appear on the same line as the expression they apply to. Otherwise, the line break will be treated as a semicolon, and the ++ or -- will be parsed as a prefix operator applied to the code that follows. Consider this code, for example:
JavaScript 将换行符解释为分号的一般规则的两个例外,当它无法将第二行解析为第一行语句的延续时。第一个异常涉及 return、break 和 continue 语句
...如果在这些单词中的任何一个之后出现换行符... JavaScript 将始终将该换行符解释为分号。
... 第二个例外涉及 ++ 和 ?? 运算符 ... 如果您想将这些运算符中的任何一个用作后缀运算符,它们必须与它们所应用的表达式出现在同一行。否则,换行符将被视为分号,++ 或 -- 将被解析为应用于后续代码的前缀运算符。考虑这个代码,例如:
x
++
y
It is parsed as
x; ++y;, not asx++; y
它被解析为
x; ++y;,而不是x++; y
So I think to simplify it, that means:
所以我认为简化它,这意味着:
In general, JavaScript will treat it as continuation of code as long as it makes sense -- except 2 cases: (1) after some keywords like return, break, continue, and (2) if it sees ++or --on a new line, then it will add the ;at the end of the previous line.
在一般情况下,JavaScript的把它当作码延续,只要它是有道理的-除了两种情况:(1)经过一些关键字时return,break,continue,和(2)如果看到++或者--在新的一行,则反而会加重该;在前一行的末尾。
The part about "treat it as continuation of code as long as it makes sense" makes it feel like regular expression's greedy matching.
关于“只要有意义就将其视为代码的延续”的部分让人感觉像是正则表达式的贪婪匹配。
With the above said, that means for returnwith a line break, the JavaScript interpreter will insert a ;
如上所述,这意味着对于return换行符,JavaScript 解释器将插入一个;
(quoted again: If a line break appears after any of these words [such as return] ... JavaScript will always interpret that line break as a semicolon)
(再次引用:如果在这些单词中的任何一个之后出现换行符 [例如return] ... JavaScript 将始终将该换行符解释为分号)
and due to this reason, the classic example of
由于这个原因,经典的例子
return
{
foo: 1
}
will not work as expected, because the JavaScript interpreter will treat it as:
不会按预期工作,因为 JavaScript 解释器会将其视为:
return; // returning nothing
{
foo: 1
}
There has to be no line-break immediately after the return:
在 之后必须没有换行符return:
return {
foo: 1
}
for it to work properly. And you may insert a ;yourself if you were to follow the rule of using a ;after any statement:
使其正常工作。;如果您要遵循;在任何语句之后使用 a 的规则,您可以自己插入 a :
return {
foo: 1
};
回答by Dexygen
Regarding semicolon insertion and the var statement, beware forgetting the comma when using var but spanning multiple lines. Somebody found this in my code yesterday:
关于分号插入和 var 语句,请注意在使用 var 但跨越多行时忘记逗号。昨天有人在我的代码中发现了这个:
var srcRecords = src.records
srcIds = [];
It ran but the effect was that the srcIds declaration/assignment was global because the local declaration with var on the previous line no longer applied as that statement was considered finished due to automatic semi-colon insertion.
它运行了,但效果是 srcIds 声明/赋值是全局的,因为前一行带有 var 的本地声明不再适用,因为由于自动分号插入,该语句被认为已完成。
回答by jchook
The most contextual description of JavaScript's Automatic Semicolon InsertionI have found comes from a book about Crafting Interpreters.
我发现的 JavaScript自动分号插入的最上下文描述来自一本关于Crafting Interpreters的书。
JavaScript's “automatic semicolon insertion” rule is the odd one. Where other languages assume most newlines are meaningful and only a few should be ignored in multi-line statements, JS assumes the opposite. It treats all of your newlines as meaningless whitespace unless it encounters a parse error. If it does, it goes back and tries turning the previous newline into a semicolon to get something grammatically valid.
JavaScript 的“自动分号插入”规则很奇怪。其他语言假定大多数换行符是有意义的,并且在多行语句中只有少数应该被忽略,而 JS 则假设相反。除非遇到解析错误,否则它将所有换行符视为无意义的空格。如果是,它会返回并尝试将前一个换行符转换为分号以获得语法上有效的内容。
He goes on to describe it as you would code smell.
他继续将其描述为代码气味。
This design note would turn into a design diatribe if I went into complete detail about how that even works, much less all the various ways that that is a bad idea. It's a mess. JavaScript is the only language I know where many style guides demand explicit semicolons after every statement even though the language theoretically lets you elide them.
如果我详细说明它是如何工作的,那么这个设计说明就会变成设计谩骂,更不用说这是一个坏主意的所有各种方式。一团糟。JavaScript 是我所知道的唯一一种语言,许多样式指南要求在每个语句后显式分号,即使该语言理论上允许您省略它们。
回答by nmxl
Just to add,
补充一下,
const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
console.log("aa");
})()
see this, using immediately invoked function expression(IIFE)
看到这个,使用立即调用的函数表达式(IIFE)

