最近因為分號的疏忽,導致出現了一個bug,記錄下來,分享給大家。
1、一個示例
給你下面這一段代碼,你根據經驗判斷一下運營結果
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’ ?
可實際呢?咱們跑一下看看:
console.log([x,y]) // ['b', 'a']
console.log([a,b]) // ['a','b']
2、為什么呢?
這段代碼不加分號會導致 [x,y]
和 [a,b]
被錯誤解析為一個連續表達式,從而引發賦值錯誤。根本原因是 JavaScript 的 自動分號插入(ASI) 機制在以下情況 不會插入分號:
- 當下一行以
[
開頭時,會被解析為當前語句的延續 - 賦值表達式可以跨行解析
3、錯誤解析過程(無分號時):
if(x < y){[x,y] = [y,x] // 注意這里沒有分號[a,b] = [b,a] // 被解析為上一行的延續
}
JavaScript 引擎會將其解析為:
[x,y] = [y,x][a,b] = [b,a]
這實際等價于:
// 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、加分號后驗證代碼:
let [a,b] = ['a','b']; // a='a', b='b'
let [x,y] = [1,2]; // x=1, y=2if(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. 始終添加分號(推薦)
// 正確寫法
[x,y] = [y,x]; // 明確分號結束
[a,b] = [b,a];
5.2. 使用防御性分號
// 在可能引起歧義的語句前加分號
;[a,b] = [b,a]
5.3. 用逗號寫成單行
// 單行寫法避免換行問題
if(x < y){ [x,y] = [y,x], [a,b] = [b,a] }
6、關鍵教訓:
-
避免以
[
或(
開頭的行
這類語法結構容易與前一行的表達式粘連 -
解構賦值后必須加分號
特別是在塊語句(if/for
等)內部 -
使用 ESLint 規則
配置semi: ["error", "always"]
強制分號使用:// .eslintrc.json {"rules": {"semi": ["error", "always"]} }