重訪 Javascript 臭名昭著的循環問題
Javascript 中臭名昭著的循環問題繼續困擾著開發人員。考慮以下程式碼片段:
function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function () { alert(i); }; document.body.appendChild(link); } }
此程式碼旨在建立 5 個鏈接,每個連結都有一個顯示當前連結 ID 的 onClick 事件。然而,當點擊這些連結時,它們都顯示「連結 5」。
這個問題的根本原因在於 Javascript 的函數級作用域。當迴圈完成時, i 變數保留值 5。這是因為 Javascript 函數在其詞法環境中是封閉的,這意味著它們可以存取周圍範圍中定義的變數。
此問題的解決方法是引入一個閉包,捕獲每次迭代的i 的當前值:
function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function (num) { return function () { alert(num); }; }(i); document.body.appendChild(link); } }
在此程式碼中,為每個連結建立一個新的函數對象。它有自己的作用域和一個局部變數 num,它被賦予 i 的當前值。執行內部函數時,它引用閉包的 num 變量,該變量保留了循環中分配的正確值。
雖然這種方法很有效,但在建立新函數時會帶來效能損失對於每個事件偵聽器。更有效率的替代方案是使用 DOM 節點本身來儲存資料:
function linkListener() { alert(this.i); } function addLinks () { for(var i = 0; i < 5; ++i) { var link = document.createElement('a'); link.appendChild(document.createTextNode('Link ' + i)); link.i = i; link.onclick = linkListener; document.body.appendChild(link); } }
透過將 i 值直接儲存在 DOM 節點上,我們消除了對閉包的需要,從而提高了程式碼效率。
以上是為什麼 Javascript 迴圈會導致事件處理程序出現意外行為?的詳細內容。更多資訊請關注PHP中文網其他相關文章!