問題代碼
$(document).ready(function(){
for(var i = 0; i < 5; i++){
$("#b"+i).bind("click", function(){
alert(i); // 全部彈出 5
});
}
});
根本原因
變量提升 + 閉包捕獲引用:
- `var i` 被提升到函數頂部,整個循環共享**同一個** `i` 變量
- 點擊事件回調形成閉包,**引用**外層變量 `i`,而非複製其值
- 循環結束時 `i=5`,點擊時讀取的是最終值 5
解決方案對比
| 方案 | 代碼 | 原理 | 推薦度 |
| 1. IIFE(立即執行函數) | for(var i=0; i<5; i++){
(function(idx){
$("#b"+idx).click(function(){ alert(idx); });
})(i);
}
| 創建新作用域,捕獲當前值副本 | ⭐⭐⭐ 經典兼容 |
| 2. let 塊級作用域 (ES6+) | for(let i=0; i<5; i++){
$("#b"+i).click(function(){ alert(i); });
}
| 每次迭代創建新綁定,天然解決 | ⭐⭐⭐⭐⭐ 現代首選 |
| 3. jQuery data屬性 | for(var i=0; i<5; i++){
$("#b"+i).data("index", i).click(function(){
alert($(this).data("index"));
});
}
| 將值存儲在DOM元素上 | ⭐⭐ 適用簡單場景 |
| 4. 箭頭函數 + 參數默認值 | for(var i=0; i<5; i++){
$("#b"+i).click(((idx)=>()=>alert(idx))(i));
}
| 利用默認參數捕獲當前值 | ⭐⭐ 技巧性較強 |
深度理解:閉包與作用域鏈
// 等價於編譯器看到的代碼結構
function setup() {
var i; // 提升到函數頂部
for(i=0; i<5; i++) {
// 這裡創建的函數對象,其[[Scope]]指向同一個包含變量i的活動對象
var callback = function() { alert(i); };
$("#b"+i).bind("click", callback);
}
// 循環結束 i=5,所有callback引用的i都是同一個變量,值為5
}
最佳實踐建議
- 現代項目**直接使用 `let`/`const`**,徹底避免此類問題
- 舊項目(IE兼容)使用 **IIFE** 或 **jQuery.data()**
- Eslint 規則 `no-loop-func` 可自動檢測此風險
- 上一篇文章: Eclipse ADT與SDK版本兼容性指南:避坑安裝與鏈接推薦
- 下一篇文章: Java Swing鍵盤事件長按觸發機制:KeyPressed重複觸發條件
Copyright ©
程式碼之家 All Rights Reserved