自动分号插入和返回语句

IT技术 javascript specifications ecmascript-5 automatic-semicolon-insertion
2021-01-17 16:10:33

您可能知道,ECMAscript 试图变得聪明,如果您没有明确地写分号,它会自动插入分号简单的例子

function foo() {
    var bar = 5

    return bar
}

仍将按预期工作。但是如果你依赖它,有一些警告。如果我们像这样重写那个函数

function foo() {
    var bar = 5

    return
    {
        bar: bar
    }
}

..那个函数现在会返回,undefined因为解释器会在return语句之后立即插入那个分号(这就是为什么你总是应该在语句的同一行上加上大括号的原因)。

然而,知道这一切,现在我不知道如何安全的一个return像下面的语句是,跨浏览器和版本

function foo() {
    var a = true,
        b = true,
        c = false;

    return a 
            && b
            && c;
}

我刚刚return statement在生产环境中写了一个类似的仅仅因为我知道 ECMAscript 的“问题”对分号插入不太聪明,我现在想知道该代码是否 100% 有效。在我对 FF/Chrome/IE(最新版本)的第一次测试中,这似乎完全没问题,但真的是这样吗?

如果除了该行中语句之外还有其他内容自动分号插入是否会“唤醒” return任何人都可以提供有关此的实现级别的详细信息吗?

3个回答

javascript 解释器/编译器非常聪明,只有在之后有有效的 Javascript 时才插入自动分号。

您的代码有效,因为&& b目前没有有效的表达式 - 这就是为什么在return a结果之后没有插入分号

return a && b && c;

然而:

return (undefined);//implicitely inserted
{
    ....
}

是完全有效的,这就是插入分号的原因。

为了完整起见,参考规范:自动分号插入这些例子值得一读。

有一些小型移动浏览器仅支持有限的 ecmascript 子集,但这些大多是特殊功能。但是,我非常有信心规范的这些“基本”部分在浏览器中的实现非常一致。
2021-03-23 16:10:33
感谢您的回复 - 我已经找到了规范的那部分,但是,我仍然想知道该特定部分对(移动)浏览器和版本的实现有多好/正确,以及我们是否可以 100% 依赖该行为。
2021-03-30 16:10:33
至少在 Chrome 中,声称javascript 解释器/编译器非常聪明,只有在之后有有效的 Javascript 时才插入自动分号。是假的。例如,以 OP 的示例为例,将密钥bar用引号括起来,使其读取'bar'. 现在,打开大括号与返回在同一行,一切正常;在它们之间放置换行符时,自动分号插入会产生语法错误。
2021-04-04 16:10:33
@MarkAmery 我知道这是一个轻微的简化。感兴趣的人可能会阅读规范,对于所有其他人,这涵盖了 90% 的用例。
2021-04-07 16:10:33

没有浏览器/ implentation具体,但Section 7.9 Automatic Semicolon Insertion在的ECMAScript语言规范是值得一读。

7.9 自动分号插入

某些 ECMAScript 语句(空语句、变量语句、表达式语句、do-while 语句、continue 语句、break 语句、return 语句和 throw 语句)必须以分号终止。此类分号可能始终明确出现在源文本中。但是,为方便起见,在某些情况下,源文本中可能会省略此类分号。这些情况的描述是说在这些情况下分号会自动插入到源代码令牌流中。

7.9.1 自动分号插入规则 分号插入有三个基本规则:

  1. 当程序从左到右解析时,遇到任何语法产生式都不允许的记号(称为违规记号),如果出现以下一项或多项情况,则会在违规记号前自动插入分号条件为真:

    • 至少有一个 LineTerminator 将违规令牌与前一个令牌分开。
    • 违规令牌是}。
  2. 当程序从左到右解析时,遇到令牌输入流的末尾并且解析器无法将输入令牌流解析为单个完整的 ECMAScript 程序时,则自动在末尾插入分号输入流。

  3. 当程序从左到右解析时,遇到语法的某些产生式允许的标记,但该产生式是受限产生式并且该标记将是紧跟注释之后的终结符或非终结符的第一个标记?[这里没有 LineTerminator]?在受限制的产生式中(因此这样的标记称为受限制的标记),并且受限制的标记与前一个标记至少通过一个 LineTerminator 分隔,然后在受限制的标记之前自动插入分号。但是,前面的规则有一个额外的覆盖条件:如果分号随后将被解析为空语句,或者该分号将成为 for 语句标头中的两个分号之一,则永远不会自动插入分号(请参阅12.6.3)。注意以下是语法中唯一受限制的产生式: PostfixExpression : LeftHandSideExpression [此处没有 LineTerminator] ++ LeftHandSideExpression [此处没有 LineTerminator] -- ContinueStatement : continue [此处没有 LineTerminator] Identifier ; BreakStatement : break [此处没有 LineTerminator] 标识符;ReturnStatement : return [此处没有 LineTerminator] 表达式;ThrowStatement : throw [这里没有 LineTerminator] 表达式;这些受限产生式的实际效果如下:当遇到 ++ 或 -- 标记时解析器会将其视为后缀运算符,并且在前面的标记和 ++ 或 -- 标记之间至少出现一个 LineTerminator , 然后在 ++ 或 -- 标记之前自动插入一个分号。当继续、中断、返回时,或 throw 标记并且在下一个标记之前遇到 LineTerminator,在 continue、break、return 或 throw 标记之后自动插入分号。对 ECMAScript 程序员的实用建议是: 后缀 ++ 或 -- 运算符应与其操作数出现在同一行。return 或 throw 语句中的表达式应该与 return 或 throw 标记在同一行开始。break 或 continue 语句中的标识符应与 break 或 continue 标记在同一行。return 或 throw 语句中的表达式应该与 return 或 throw 标记在同一行开始。break 或 continue 语句中的标识符应与 break 或 continue 标记在同一行。return 或 throw 语句中的表达式应该与 return 或 throw 标记在同一行开始。break 或 continue 语句中的标识符应与 break 或 continue 标记在同一行。

7.9.2 自动分号插入示例

来源

{ 1 2 } 3

不是 ECMAScript 语法中的有效句子,即使有自动分号插入规则。相比之下,源

{ 1
2 } 3

也不是有效的 ECMAScript 语句,而是通过自动分号插入转换为以下内容:

{ 1
;2 ;} 3;

这是一个有效的 ECMAScript 语句。来源

for (a; b
)

不是有效的 ECMAScript 语句,并且不会被自动分号插入更改,因为 for 语句的标题需要分号。自动分号插入永远不会在 for 语句的标题中插入两个分号之一。来源

return
a + b

通过自动分号插入转换为以下内容:

return;
a + b;

注意表达式 a + b 不被视为返回语句返回的值,因为 LineTerminator 将它与标记返回分开。来源

a = b
++c

通过自动分号插入转换为以下内容:

a = b;
++c;

注意 标记 ++ 不被视为应用于变量 b 的后缀运算符,因为 LineTerminator 出现在 b 和 ++ 之间。来源

if (a > b)
else c = d

不是有效的 ECMAScript 语句,并且不会被 else 标记之前的自动分号插入更改,即使此时不适用语法的产生式,因为自动插入的分号随后将被解析为空语句。来源

a = b + c
(d + e).print()

不会通过自动分号插入进行转换,因为第二行开始的括号表达式可以解释为函数调用的参数列表:

a = b + c(d + e).print()

在赋值语句必须以左括号开头的情况下,程序员最好在前面的语句末尾提供一个明确的分号,而不是依赖于自动插入分号。

正如 Christoph 指出的那样,您的 return 语句将在所有浏览器中正常工作。我更喜欢让它更明确,如果不是对于计算机而是对于人类,至少通过将 和 运算符放置不同的方式:

return a &&
       b &&
       c;

在这种情况下,没有人需要花一秒钟来怀疑自动分号是否会造成严重破坏。我只对 JavaScript 更喜欢这个,你的原始代码更容易阅读。