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

好吧,首先我应该问一下这是否与浏览器有关。

我已经读到,如果找到了无效的令牌,但是代码段在该无效令牌之前一直有效,如果在该令牌之前加了换行符,则在该令牌之前插入一个分号。

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

return
  _a+b;

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

另一方面,分解呼叫链可以按预期工作:

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

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

Tom老丝Pro2020/03/16 12:05:15

我发现对JavaScript的自动分号插入的上下文描述最多来自一本有关制作解释器的书

JavaScript的“自动分号插入”规则很奇怪。在其他语言假定大多数换行符有意义且多行语句中应忽略少数几种的情况下,JS则相反。除非遇到解析错误,否则它将所有换行符视为无意义的空格。如果是这样,它将返回并尝试将前一个换行符转换为分号以获取语法上有效的内容。

他继续描述它,就像您会编码气味一样

如果我详细介绍了它的工作原理,那么本设计笔记将变成一个设计思路,更不用说所有的各种坏方法了。一团糟。我知道JavaScript是唯一的一种语言,尽管从理论上讲,您可以忽略每条语句,但许多样式指南在每条语句后都要求使用明确的分号。

村村A小卤蛋2020/03/16 12:05:15

直接来自ECMA-262,第五版ECMAScript规范

7.9.1自动分号插入规则

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

  1. 当从左到右解析程序时,遇到任何语法生成都不允许的令牌(称为有问题的令牌),则如果以下一项或多项操作,则在有问题的令牌之前自动插入分号条件为真:
    • 有问题的令牌与先前的令牌之间至少相隔一个LineTerminator
    • 令人讨厌的令牌是}
  2. 当从左向右解析程序时,遇到令牌输入流的末尾并且解析器无法将输入令牌流作为单个完整的ECMAScript进行解析Program,则分号将自动插入到末尾。输入流。
  3. 当从左到右解析程序时,遇到某种语法的某种生产所允许的令牌,但是该生产是受限制的生产,并且该令牌将成为紧随注解之后的终端或非终端的第一个令牌在受限产品中使用[no LineTerminatorhere] ”(因此这种令牌称为受限令牌),并且受限令牌由至少一个LineTerminator与前一个令牌分隔开,然后在受限令牌之前自动插入分号。

但是,上述规则还有一个额外的优先条件:如果分号随后将被解析为空语句,或者该分号将成为for语句标题中的两个分号之一,则永远不会自动插入分号。.3)。

别坑我路易2020/03/16 12:05:15

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

  • 空语句
  • var 声明
  • 表达陈述
  • do-while 声明
  • continue 声明
  • break 声明
  • return 声明
  • throw 声明

有关ASI的具体规则,请参见规范§11.9.1自动分号插入规则

描述了三种情况:

  1. 遇到语法不允许的记号(LineTerminator})时,如果出现以下情况,将在其前插入分号:

    • 令牌与上一个令牌分隔至少一个LineTerminator
    • 令牌是 }

    例如

    { 1
    2 } 3
    

    转化为

    { 1
    ;2 ;} 3;
    

    NumericLiteral 1满足所述第一条件,令牌是行终止如下。
    2满足第二条件,令牌是以下}

  2. 当遇到令牌输入流的末尾并且解析器无法将输入令牌流作为单个完整程序进行解析时,则分号会自动插入到输入流的末尾。

    例如

    a = b
    ++c
    

    转换为:

    a = b;
    ++c;
    
  3. 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";
    
小哥Tony2020/03/16 12:05:15

关于分号插入和var语句,请注意在使用var时要忘记逗号,但要跨多行。昨天有人在我的代码中找到了这个:

    var srcRecords = src.records
        srcIds = [];

它运行了,但结果是srcIds声明/赋值是全局的,因为由于自动分号插入导致该语句被视为完成,因此不再应用前一行上带有var的本地声明。