最近因为分号的疏忽,导致出现了一个bug,记录下来,分享给大家。
1、一个示例
给你下面这一段代码,你根据经验判断一下运营结果
scss
let [a,b] = ['a','b']
let [x,y] = [1,2]
if(x < y){
[x,y] = [y,x]
[a,b] = [b,a]
}
按照一般的理解,是不是应该是 x = 2,y=1,a = 'b',b = 'a' ? 可实际呢?咱们跑一下看看:
css
console.log([x,y]) // ['b', 'a']
console.log([a,b]) // ['a','b']
2、为什么呢?
这段代码不加分号会导致 [x,y]
和 [a,b]
被错误解析为一个连续表达式,从而引发赋值错误。根本原因是 JavaScript 的 自动分号插入(ASI) 机制在以下情况 不会插入分号:
- 当下一行以
[
开头时,会被解析为当前语句的延续 - 赋值表达式可以跨行解析
3、错误解析过程(无分号时):
javascript
if(x < y){
[x,y] = [y,x] // 注意这里没有分号
[a,b] = [b,a] // 被解析为上一行的延续
}
JavaScript 引擎会将其解析为:
javascript
[x,y] = [y,x][a,b] = [b,a]
这实际等价于:
javascript
// 1. 先计算 [y,x][a,b]
// - [y,x] 是一个数组 [2, 1]
// - [a,b] 是逗号表达式,返回最后一个值 'b'
const temp = [y,x]['b'] // 相当于访问数组的 'b' 属性
// 2. 将 [b,a] 赋值给上述结果
temp = [b,a]
// 再将 temp 赋值给 [x,y]
[x,y] = temp
// 也就是
[x,y] = [b,a]
最终导致 x
和 y
被赋值为 [b,a]
(即 ['b','a']
)
4、加分号后验证代码:
javascript
let [a,b] = ['a','b']; // a='a', b='b'
let [x,y] = [1,2]; // x=1, y=2
if(x < y){
[x,y] = [y,x]; // 正常交换:x=2, y=1
[a,b] = [b,a]; // 正常交换:a='b', b='a'
}
console.log(x, y); // 输出: 2 1
console.log(a, b); // 输出: b a
5、解决方案:
5.1. 始终添加分号(推荐)
javascript
// 正确写法
[x,y] = [y,x]; // 明确分号结束
[a,b] = [b,a];
5.2. 使用防御性分号
javascript
// 在可能引起歧义的语句前加分号
;[a,b] = [b,a]
5.3. 用逗号写成单行
javascript
// 单行写法避免换行问题
if(x < y){ [x,y] = [y,x], [a,b] = [b,a] }
6、关键教训:
-
避免以
[
或(
开头的行这类语法结构容易与前一行的表达式粘连
-
解构赋值后必须加分号
特别是在块语句(
if/for
等)内部 -
使用 ESLint 规则
配置
semi: ["error", "always"]
强制分号使用:json// .eslintrc.json { "rules": { "semi": ["error", "always"] } }