在 JavaScript 中使用 for...in 遍歷類數組對象(如 document.getElementsByTagName("img") 返回的 HTMLCollection)並為每個元素綁定事件處理器時,新手常遇到「循環變量在回調中值不正確」的經典問題。本文通過一個圖片鼠標懸停示例,深入剖析原因並給出閉包解決方案。
var imgs = document.getElementsByTagName("img");
for (var item in imgs) {
imgs[item].onmouseover = function() {
alert(item); // 彈出的總是最後一個索引值
};
}
現象:無論滑過哪張圖片,彈出的 item 值都是循環結束時的最後一個值(如 "length" 或最後一個數字索引)。
var 聲明的變量具有**函數作用域**,無塊級作用域。循環中的 item 在整個函數內僅有一個實例。item 保留最後賦予的值。item 變量,故均讀到最終值。利用函數作用域為每次循環創建獨立的變量副本:
var imgs = document.getElementsByTagName("img");
for (var item in imgs) {
(function(index) {
imgs[index].onmouseover = function() {
alert(index); // 正確彈出當前索引
};
})(item);
}
原理:IIFE 立即執行,將當前 item 值作為參數 index 傳入,形成獨立的詞法作用域。每個事件處理器引用各自的 index,互不干擾。
let 替代 var:let 具有塊級作用域,循環每次迭代都會創建新的綁定,天然解決此問題。
for (let item of imgs) { // 或 for (let i = 0; i < imgs.length; i++)
imgs[i].onmouseover = function() { alert(i); };
}
forEach / Array.from 配合箭頭函數。更多關於閉包、作用域鏈、異步編程的深度解析,可參考:JavaScript 閉包詳解。