【问题标题】:Automatic semicolon insertion & return statements [duplicate]自动分号插入和返回语句[重复]
【发布时间】:2012-09-26 13:54:57
【问题描述】:

您可能知道,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 语句之外还有其他内容,自动分号插入会“唤醒”吗?任何人都可以提供有关此的实现级别的详细信息吗?

【问题讨论】:

标签: javascript specifications ecmascript-5 automatic-semicolon-insertion


【解决方案1】:

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

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

return a && b && c;

但是:

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

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

为了完整起见,请参考规范:automatic semicolon insertion。这些例子值得一读。

【讨论】:

  • 感谢您的回复 - 我已经找到了规范的那个部分,但是,我仍然想知道该特定部分对于(移动)浏览器和版本的实现有多好/正确,如果我们可以 100 % 依赖于这种行为。
  • 有些小型移动浏览器仅支持有限的 ecmascript 子集,但这些大多是特殊功能。但是,我非常有信心,规范的这些“基本”部分在浏览器中的实现非常一致。
  • 至少在 Chrome 中,声称 javascript 解释器/编译器非常聪明,只有在之后有有效的 Javascript 时才插入自动分号。 是错误的。例如,以 OP 为例,将密钥 bar 括在引号中,使其读取为 'bar'。现在,开括号与返回在同一行,一切正常;在它们之间加上换行符,自动分号插入会产生语法错误。
  • @MarkAmery 我知道这是一个轻微的简化。感兴趣的人可能会阅读规范,对于所有其他人来说,这涵盖了 90% 的用例。
  • 我知道已经十年了,但上面的解释是不正确的。在return a [endline] && b 中的&& 之前没有插入分号的原因是因为标记&& 在该位置已经有效 而无需添加分号,而不是因为添加分号会产生结果无效。而当结束线直接出现在 return 关键字之后时,这已经是无效的,因为该位置不允许有结束线。所以在这种情况下会插入一个分号。
【解决方案2】:

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

return a &&
       b &&
       c;

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

【讨论】:

    【解决方案3】:

    不特定于浏览器/实现,但ECMAScript Language Specification 中的Section 7.9 Automatic Semicolon Insertion 值得一读。

    7.9 自动分号插入

    某些 ECMAScript 语句(空语句、变量语句、表达式语句、do-while 语句、continue 语句、break 语句、return 语句和 throw 语句)必须是 以分号结尾。这样的分号可能总是显式地出现在源文本中。为了 但是,为了方便起见,在某些情况下,可以从源文本中省略这些分号。这些 通过说分号自动插入源代码令牌来描述情况 在这些情况下进行流式传输。

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

    1. 当程序从左到右解析时,遇到一个标记(称为违规标记) 语法的任何产生式都不允许,则在 如果以下一个或多个条件为真,则违规令牌:

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

    3. 当程序从左到右解析时,遇到某些允许的标记 语法的产生,但产生是受限制的产生,令牌将是第一个 紧跟注释后的终端或非终端的标记?[此处没有 LineTerminator]?内 受限生产(因此这样的令牌称为受限令牌),并且受限令牌是 由至少一个 LineTerminator 与前一个标记分隔,然后自动使用分号 在受限令牌之前插入。 但是,前面的规则还有一个额外的覆盖条件:从不插入分号 如果分号随后将被解析为空语句或该分号将变为 for 语句头中的两个分号之一(见 12.6.3)。 注意以下是语法中唯一受限制的产生式: 后缀表达式: LeftHandSideExpression [此处没有 LineTerminator] ++ LeftHandSideExpression [这里没有 LineTerminator] -- 继续声明: continue [这里没有 LineTerminator] 标识符; 断句: break [此处没有 LineTerminator] 标识符; 退货声明: return [这里没有 LineTerminator] 表达式; 抛出语句: throw [这里没有 LineTerminator] 表达式; 这些限制生产的实际效果如下: 当遇到解析器将其视为后缀运算符的 ++ 或 -- 标记时,并且至少有一个 LineTerminator 出现在前面的标记和 ++ 或 -- 标记之间,然后自动分号 在 ++ 或 -- 标记之前插入。 当遇到 continue、break、return 或 throw 标记并且之前遇到 LineTerminator 下一个标记,在 continue、break、return 或 throw 标记之后自动插入分号。 给 ECMAScript 程序员的实用建议是: 后缀 ++ 或 -- 运算符应与其操作数出现在同一行。 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 语句要返回的值,因为 a 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-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 1970-01-01
      • 2021-03-14
      • 1970-01-01
      相关资源
      最近更新 更多