15

您可能知道,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任何人都可以提供有关此的实现级别的详细信息吗?

4

3 回答 3

13

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

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

return a && b && c;

然而:

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

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

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

于 2012-10-05T12:31:08.903 回答
2

不是特定于浏览器/实现的,但Section 7.9 Automatic Semicolon InsertionECMAScript语言规范值得一读。

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 [no LineTerminator here] ++ LeftHandSideExpression [no LineTerminator here] -- ContinueStatement : continue [no LineTerminator here] Identifier ; BreakStatement : break [no LineTerminator here] Identifier ; ReturnStatement : return [no LineTerminator here] Expression ; ThrowStatement : throw [no LineTerminator here] Expression ; 这些限制产生式的实际效果如下:当遇到一个 ++ 或 -- 标记时,解析器会将其视为后缀运算符,并且在前面的标记和 ++ 或 -- 标记之间至少出现了一个 LineTerminator , 然后在 ++ 或 -- 标记之前自动插入一个分号。当一个继续,中断,返回,或者遇到 throw 标记并且在下一个标记之前遇到 LineTerminator,在 continue、break、return 或 throw 标记之后会自动插入一个分号。对 ECMAScript 程序员的实际建议是:后缀 ++ 或 -- 运算符应与其操作数出现在同一行。return 或 throw 语句中的 Expression 应与 return 或 throw 标记在同一行开始。break 或 continue 语句中的标识符应与 break 或 continue 标记位于同一行。return 或 throw 语句中的 Expression 应与 return 或 throw 标记在同一行开始。break 或 continue 语句中的标识符应与 break 或 continue 标记位于同一行。return 或 throw 语句中的 Expression 应与 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 不被视为要由 return 语句返回的值,因为 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()

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

于 2012-10-05T12:37:27.743 回答
0

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

return a &&
       b &&
       c;

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

于 2012-10-05T12:35:45.000 回答