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