JavaScript 中的分号自动插入


prtyaa
prtyaa 2023-12-26 17:54:29 67635
分类专栏: 资讯
  • 许多编程语言都有语句结束的概念。不过,用哪个符号结束以及结束语句的规则在各个语言之间存在差异。

    对于 JavaScript 来说,它在这方面要求非常宽松。在 JavaScript 中语句结束符是分号,不过也可以不写。它是怎么做到的?JavaScript 编译器是如何知道何时该结束一条语句呢?

    它是通过一套简单的规则和自动分号插入机制(Automatic Semicolon Insertion,简写为 ASI)实现的。我们先看一下分号的规则,然后了解一下 ASI 的机制。

    分号的规则

    分号用于标记一条语句的结束。

    // Variable Declaration
    const myVar = 5;
    // Invoking a function
    myFunction();
    

    块语句的末尾应该有一个分号。

    // function definition
    function myFunction() {
      // function logic
    }
    // A conditional `if` block
    if (myCondition) {
      // conditional logic
    }
    // loop constructs
    while (myCondition) {
      // conditional logic
    }
    

    分号的特殊案例。

    const myFunction = function() {};
    

    上面的代码是一个函数表达式,所以需要一个分号。

    自动分号插入(Automatic Semicolon Insertion)

    这是 Javascript 中的一种机制,它遵循一些规则,编译器会尝试把源代码分解成语句。默认情况下,ASI 总是打开的。有时候自动分号插入机制很有用,但偶尔会遇到 ASI 改变语义引起的棘手问题。

    ASI 规则

    1. 遇到行结束符时,会插入一个分号。

    // 实际的源码
    const myVar = 5
    // ASI 之后
    const myVar = 5;
    

    2.遇到句法不允许的 ‘}’ 时插入一个分号。

    { 1
    2 } 3
    
    // ASI 后变成了
    
    { 1
    ;2 ;} 3;
    

    3.遇到一个 restricted production 后跟行结束符时,自动插入一个分号。

    这些 restricted production 包括:++, --, continue, break, return, throw, yield 和 module 关键字。解析器遇到这些关键字后跟一个行结束符时,会在关键字后面插入一个分号。

    // 实际的源码
    for (let i = 0; i < 5; i++) {
      if (myCondition) {
        continue
      }
    }
    // ASI 后
    for (let i = 0; i < 5; i++) {
      if (myCondition) {
        continue;
      }
    }
    

    但是需要注意 return 关键字:

    return
    a + b
    
    // ASI 后变成了
    
    return;
    a + b;
    

    上述代码会在 return 语句后面插入一个分号,它会返回 undefined。return 后面的语句无法被访问。为了避免这个问题,可以把上述2条语句写在同一行。

    依赖 ASI 可能导致的意外问题

    如果我们不在代码中写分号,而是依赖 ASI,会偶尔遇到语义完全改变的情况。

    意外的函数调用

    // 实际的源码
    const myResult = myVar1 + myVar2
    (myVar3 + myVar4).toString()
    // 期待的输出
    const myResult = myVar1 + myVar2;
    (myVar3 + myVar4).toString();
    // ASI 后实际的输出
    const myResult = myVar1 + myVar2(myVar3 + myVar4).toString()
    

    意外的属性访问

    // 实际的源码
    const myResult = myFunction()
    ['ul', 'ol'].map(x => x + 1)
    // 期望的输出
    const myResult = myFunction();
    ['ul', 'ol'].map(x => x + 1)
    // ASI 后实际的输出
    const myResult = myFunction()[("ul", "ol")].map(x => x + 1);
    

    结语

    现在很多人选择忽略分号,让代码看起来更简洁。不过我建议始终明确写上分号,以避免引起潜在的问题。

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=30948
赞同 0
评论 0 条