JavaScript自动插入(ASI)的规则是什么?

发布于 2025-02-08 12:57:20 字数 379 浏览 2 评论 0 原文

好吧,首先,我应该问这是否是浏览器依赖性的。

我已经读到,如果找到一个无效的令牌,但是代码部分有效,直到该代币无效,如果在令牌之前插入了一个半隆之前,则在该令牌之前插入了一个semicolon。

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

return
  _a+b;

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

另一方面,分解呼叫链的工作原理:

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

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

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.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

南风起 2025-02-15 12:57:20

首先,您应该知道哪些语句受自动插入的影响(也称为简洁的ASI):

  • 空语句
  • var var 语句
  • express stress stress
  • do-while
  • 语句继续语句
  • break 语句
  • <代码>返回语句
  • throw 语句

ASI的具体规则,在规范

描述了三种情况:

  1. 当遇到语法不允许的违规令牌时,如果以下情况,则在其之前插入半分离术:
  • 将令牌与以前的令牌分开至少一个 lineTerminator
  • 令牌是}

eg

    { 1
    2 } 3

已转换为

    { 1
    ;2 ;} 3;

numericLiteral 1 符合第一个条件,以下令牌是线终结者。
2 符合第二个条件,以下令牌为}

  1. 当遇到令牌的输入流的末端,而解析器无法将输入令牌流解析为单个完整程序,则将自动插入输入流的末尾。

eg

    a = b
    ++c

已转化为:

    a = b;
    ++c;
  1. 这种情况是在某些语法的产生允许的时发生的,但是生产是限制的生产,在上,将自动插入一个半元素。限制令牌。

限制作品:

    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";

First of all you should know which statements are affected by the automatic semicolon insertion (also known as ASI for brevity):

  • empty statement
  • var statement
  • expression statement
  • do-while statement
  • continue statement
  • break statement
  • return statement
  • throw statement

The concrete rules of ASI, are described in the specification §11.9.1 Rules of Automatic Semicolon Insertion

Three cases are described:

  1. When an offending token 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 } 3

is transformed to

    { 1
    ;2 ;} 3;

The NumericLiteral 1 meets the first condition, the following token is a line terminator.
The 2 meets the second condition, the following token is }.

  1. 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
    ++c

is transformed to:

    a = b;
    ++c;
  1. 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] AssignmentExpression

The classic example, with the ReturnStatement:

    return 
      "something";

is transformed to

    return;
      "something";
小梨窩很甜 2025-02-15 12:57:20

我无法很好地理解规格中的这3个规则 - 希望能拥有更普通英语的东西 - 但这是我从JavaScript中收集的内容:第6版,David Flanagan,O'Reilly,2011年:

引用:

JavaScript不会将每个线路断裂视为半圆柱:通常只有在没有半olon的情况下无法解析代码的情况下,它才会将线路断裂为分号。

另一个报价:代码

var a
a
=
3 console.log(a)

javaScript不会将第二行断裂视为半隆,因为它可以继续解析较长的语句a = 3;

和:

当JavaScript无法解释第二行以作为第一行上的语句的延续时,JavaScript解释行断裂为半洛龙的两个例外。第一个例外涉及回报,中断和继续语句

...如果在任何这些单词之后出现了线路休息... javaScript总是将线路断开为半隆。

...第二个例外涉及++和 - 运算符...如果要将这些运算符中的任何一个用作后缀运算符,则它们必须出现在与适用的表达式相同的线上。否则,断路将被视为半隆,并且++或 - 将被解析为将其应用于以下代码的前缀操作员。考虑此代码,例如:

x 
++ 
y

它被解析为 x; ++ y; ,而不是 x ++; y

因此,我认为要简化它,这意味着:

通常,JavaScript只要有意义,JavaScript将其视为持续的代码 - 除2个情况外:(1)在一些关键字之后,例如返回 break ,<代码>继续,(2)如果看到 ++ 或 - ; 。

>在新行上,它将在 “使其感觉就像是正则表达式的贪婪匹配。

与上述说明,这意味着返回在折线中,JavaScript解释器将插入;

(再次引用:如果在任何这些单词之后出现了一条折断(例如 return ] ... JavaScript总是将阵列断开解释为半隆),

并且由于这个原因,这是

return
{ 
  foo: 1
}

Will 的经典示例无法按预期工作,因为JavaScript解释器将其视为:

return;   // returning nothing
{
  foo: 1
}

后,必须立即破坏线路

return { 
  foo: 1
}

返回:才能正常工作 。如果要遵循; 在任何语句之后使用:

return { 
  foo: 1
};

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:

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.

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;

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:

x 
++ 
y

It is parsed as x; ++y;, not as 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.

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 return with a line break, the JavaScript interpreter will insert a ;

(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)

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:

return;   // returning nothing
{
  foo: 1
}

There has to be no line-break immediately after the 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:

return { 
  foo: 1
};
从来不烧饼 2025-02-15 12:57:20

直接从 ecma-262,第五版Ecmascript Specification

7.9.1自动插入规则

半插入的三个基本规则:

  1. ,当该程序从左到右解析时,遇到一个令牌(称为 cresting disken ),而语法的任何产生都不允许使用,则在任何产生的语法中都不会自动插入一个半元素。如果以下一个或多个条件是正确的,则违规令牌:
    • 违规令牌与以前的令牌分开,至少一个 lineterminator
    • 违规令牌是}
  2. 当程序从左到右解析时,遇到了令牌的输入流的末端,而解析器无法将输入令牌流解析为单个完整的Ecmascript program ,然后将半隆自动插入输入流的末尾。
  3. ,当该程序从左到右解析时,遇到了某些语法生产允许的令牌,但是生产是a 限制的生产,而代币将是第一个在限制生产中,在注释后立即使用“ [no lineterminator )的终端或非终端的令牌(因此,这种令牌称为受限令牌),而该令牌称为限制性的令牌)和受限制的令牌与以前的令牌分开至少一个 lineterminator ,然后在受限的令牌之前自动插入半隆。

但是,关于前面的规则还有一个额外的压倒条件:如果然后将半隆作为空语言解析,或者如果该半隆成为一个半隆,则绝对不会自动插入一个半隆的条件,或者该分号是否会成为a for 语句(请参阅12.6.3)。

Straight from the ECMA-262, Fifth Edition ECMAScript Specification:

7.9.1 Rules of Automatic Semicolon Insertion

There are three basic rules of semicolon insertion:

  1. 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 }.
  2. 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.
  3. 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 production and the token would be the first token for a terminal or nonterminal immediately following the annotation "[no LineTerminator here]" 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 for statement (see 12.6.3).

傲世九天 2025-02-15 12:57:20

关于半插入和var语句,请注意使用VAR时忘记逗号,但跨越多行。昨天有人在我的代码中发现了这一点:

    var srcRecords = src.records
        srcIds = [];

它运行了,但效果是SRCIDS声明/作业是全球的,因为上一行的本地声明不再应用,因为由于自动半柱插入,该声明被认为是完成的。

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 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.

痴情 2025-02-15 12:57:20

JavaScript的我发现来自一本关于

JavaScript的“自动半插入”规则是奇怪的。如果其他语言假设大多数新线是有意义的,并且在多行语句中只能忽略少数语言,则JS假定相反。除非遇到解析错误,否则它将所有新线视为毫无意义的空格。如果是这样,它会返回并尝试将以前的newline变成半隆以获得语法有效的东西。

他继续描述它,就像您代码气味

如果我完整地详细介绍了它的工作方式,那么这个设计说明就会变成设计的次数,而少于一个坏主意的所有各种方式。这是一团糟。 JavaScript是我知道的唯一一种语言,即使语言理论上可以让您掌握它们。

The most contextual description of JavaScript's Automatic Semicolon Insertion I have found comes from a book about 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.

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.

入怼 2025-02-15 12:57:20

只是为了添加,

const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
    console.log("aa");
})()

请参阅此信息,使用立即调用函数表达式(iife)

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)

暮年慕年 2025-02-15 12:57:20

JavaScript中的大多数陈述和声明必须用半柱终止,但是,为了方便程序员(较少的打字,风格偏好,较少的代码噪声,较低的代码噪声,较低的入口障碍),在某些源文本位置可能会省略半元素,并在某些源文本位置省略。运行时会根据规格中的一组规则设置自动插入半洛龙。

规范规则:如果将半隆作为空语言解析,或者该半隆将成为 for 语句的标题中的两个分号之一,则永远不会自动插入半分号。

规则1

如果JavaScript解析器遇到一个令牌,则将自动插入半分离,如果不存在半分离符,则不允许两者都允许两者都允许,并且该令牌与以前的终结者(例如,新行)(例如,新线)将令牌与以前的差异分开。闭合支架} ,或do-where循环的最终括号()。

换句话说:源文本位置无论如何都需要终止语句的源文本位置,如果省略了语句终结器(; ),则将自动插入。该规则是ASI的核心。

规则2

如果源文本不是有效的脚本或模块,则将在程序末尾插入半分析。换句话说:程序员可以省略程序中的最后一个半隆。

规则3,

如果遇到一个令牌,如果遇到一个令牌,则如果不存在半隆,则将自动插入,但在几个特殊的源文本位置之一(限制性限制的生产)中存在,这是明确禁止行的一个特殊源文本位置之一终结者是出于避免歧义的原因。

禁止在哪个线路终止器内部的限制作品是:

  • Postfix ++ 和Postfix - - (因此,Newline之后的Unary增量/减少操作员将绑定到<< em>以下(不是以前的)语句,作为前缀操作员)
  • 继续 break throw 返回,<代码>屈服
  • 箭头函数参数列表之后,以及
  • async 在async函数声明中的关键字&amp;表达式,发电机函数声明&amp;表达式&amp;方法和异步箭头函数

规格包含完整的详细信息以及遵循实际建议:

对Ecmasimpript程序员的最终实用建议是:

  • Postfix ++或 - 操作员应与其操作数相同的行。

  • 返回或投掷语句或一个表达式
    收益率表达式中的分配表达应在同一开始
    行为返回投掷 yarts 令牌。

  • 中断> Break 继续语句应与 break> break 继续< /代码>令牌。


  • 箭头函数参数的末端(S)及其 =&gt; 应在同一行上。

  • async 在异步函数或方法之前的令牌应与立即后面的立即行。


ASI GOTCHA示例

启动了``(``

与关闭括号配对时)。

开头括号字符具有多个含义。它可以描述一个表达式,也可以指示调用(例如, .log(...)不是一个函数“因为运行时尝试调用 console.log('bar')的返回值:

let a = 'foo'
console.log('bar')
(a = 'bam') 

一个解决方案,如果您通常省略了半隆,是包括一个分号以使您的意图明确:

let a = 'foo'
console.log('bar')
;(a = 'bam') // note semicolon at start of line

使用``

开头括号字符( [)具有多种含义。声明数组(与闭合括号配对时),或者可以指示数组破坏

。在 console.log('bar')的响应中,名为“ foo”的属性的价值:

let a = 'foo'
console.log('bar')
[a] = ['bam']

如果通常省略半隆,则是一个解决方案,是包括一个半隆来使您的意图变得明确。 :

let a = 'foo'
console.log('bar')
;[a] = ['bam'] // note semicolon at start of line

Most statements and declarations in JavaScript must be terminated with a semicolon, however, for the convenience of the programmer (less typing, stylistic preference, less code noise, lower barrier to entry), semicolons may be omitted in some source text locations, with the runtime automatically inserting semicolons according to a set of rules set-out in the spec.

Over-arching 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 for statement.

Rule 1

A semicolon will be automatically inserted if a token is encountered by the JavaScript parser that both would not be allowed if a semicolon did not exist, and that token is separated from the previous by one or more line terminators (eg. newlines), a closing brace }, or the final parenthesis ()) of a do-while loop.

In other words: source text locations where statements would always need to be terminated anyway for a runnable program, will have the statement terminator (;) inserted automatically if it is omitted. This rule is the heart of ASI.

Rule 2

A semicolon will be inserted at the end of the program if the source text is not otherwise a valid script or module. In other words: programmers can omit the final semicolon in a program.

Rule 3

A semicolon will be automatically inserted if a token is encountered that would normally be allowed if a semicolon did not exist, but exists within one of several special source text locations (restricted productions) that explicitly disallow line terminators within them for reasons of avoiding ambiguity.

The restricted productions inside of which line terminators are prohibited are:

  • before postfix ++ and postfix -- (so the unary increment/decrement operators after a newline will bind to the following (not previous) statement, as a prefix operator)
  • after continue, break, throw, return, yield
  • after arrow function parameter lists, and
  • after the async keyword in async function declarations & expressions, generator function declarations & expressions & methods, and async arrow functions

The spec contains the full details, plus the following practical advice:

The resulting practical advice to ECMAScript programmers is:

  • A postfix ++ or -- operator should be on the same line as its operand.

  • An Expression in a return or throw statement or an
    AssignmentExpression in a yield expression should start on the same
    line as the return, throw, or yield token.

  • A LabelIdentifier in a break or continue statement should be on the same line as the break or continue token.

  • The end of an arrow function's parameter(s) and its => should be on the same line.

  • The async token preceding an asynchronous function or method should be on the same line as the immediately following token.

And this is the best article on the Web on this subject.

ASI Gotcha Examples

Starting a line with `(`

The opening parenthesis character has multiple meanings. It can delineate an expression, or it can indicate an invocation (when paired with a closing parenthesis).

For example, the following throws "Uncaught TypeError: console.log(...) is not a function" because the runtime attempts to invoke the return value of console.log('bar'):

let a = 'foo'
console.log('bar')
(a = 'bam') 

One solution for this, if you are generally omitting semicolons, is to include a semicolon to make your intentions unambiguous:

let a = 'foo'
console.log('bar')
;(a = 'bam') // note semicolon at start of line

Starting a line with `[`

The opening bracket character ([) has multiple meanings. It can indicate an object property access, or it can indicate the literal declaration of an array (when paired with a closing bracket), or it can indicate an array destructuring.

For example, the following throws "Uncaught TypeError: Cannot set properties of undefined (setting 'foo')" because the runtime attempts to set the value of a property named 'foo' on the response of console.log('bar'):

let a = 'foo'
console.log('bar')
[a] = ['bam']

One solution for this, if you are generally omitting semicolons, is to include a semicolon to make your intentions unambiguous:

let a = 'foo'
console.log('bar')
;[a] = ['bam'] // note semicolon at start of line
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文