JS代码质量—ASI 的机制(自动插入分号)

发布时间 2023-11-02 15:37:00作者: 吴飞ff

参考:https://zhuanlan.zhihu.com/p/394561311 

JS中有一些优雅换行(美化),可以让代码的可读性更强,但是需要注意 JS引擎自动插入分号的机制 会不会 出现非预期的情况。

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);

     

JS语句美化的写法:

  • 属性方法点的写法:JS对象链式写法如果太长,可以通过换行美化写法。
    obj
      .then()
      .then()

    这种情况可以放心写,不会因为 ASI机制 发生意外问题的。

  • 连续的多个逻辑判断:
    let obj2 = obj
        && obj.data
        && obj.data.name

    这种情况可以放心写,不会因为 ASI机制 发生意外问题的。

 

总结: 

  1. 常规写JS语句一般都不会出现什么意外的问题。ASI机制一般都会结合下一行的内容再确定 换行符要不要插入分号的;但是遇到有些标识符会直接在后面的换行符插入分号。
  2. 应该插入换行符而没有插入引起的意外问题。【因为结合下一行语句导致】
    // 实际的源码
    const myResult = myFunction()
    ['ul', 'ol'].map(x => x + 1)
    
    // ASI 后实际的输出
    const myResult = myFunction()[("ul", "ol")].map(x => x + 1);
  3. 不应该插入而插入了分号引起的意外问题。【碰到特殊标识符】
    return
    a + b
    
    // ASI 后变成了
    return;
    a + b;