【JS】關於for迴圈中的click回撥函式索引值錯誤的思考
阿新 • • 發佈:2018-12-21
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <style> #list { width: 60%; height: 40vh; display: flex; padding: 0; flex-direction: row; margin: 10vh auto; border: 1px solid red; list-style: none; } #list > li { flex: 1; border: 1px solid yellow; } </style> <body> <ul id="list"> <li>1</li> <li>2</li> <li>3</li> </ul> <script> var children = document.getElementById("list").children; for (var index = 0; index < children.length; index++) { children[index].onclick = function(){ console.log(index) // 3 (陣列的length) } } </script> </body> </html>
在點選相對應的DOM元素時,按正常邏輯來說應該會列印對應的index值,但是實際情況的都為3,也就是陣列的長度最後一位,這是為什麼呢?
我的理解是:click事件只是一個指標,函式並沒有執行(值是可變的),而index的重複賦值直接重新整理了前面的值,換句話說回撥函式裡的index指向是同一個變數!!
我為什麼會得出這樣的結論呢? 其實換位思考下JS會怎麼解析這段程式碼就容易理解了
JS沒有塊級作用域一直被詬病,結合變數宣告置頂(for迴圈的index直接成為全域性變數,而不是區域性變數),而我們可以用一個非同步函式來模擬click事件(你可以稱它為未來事件或者待執行事件),由此我們可以間接模擬出函式的解析步驟....
var index; // for迴圈中的index值
index = 0;
setTimeout(function(){ // 模擬click點選事件
console.log(index) // -> 1
}, 1000)
++index; // 模擬自增
所述綜上由此可得出解決方案
- 當前值不可變( 既立即執行 )
var index; // for迴圈中的index值 index = 0; setTimeout(function(){ // 模擬click點選事件 console.log(index) // -> 0, 1, 2 }, 1000) ++index; //自增
立即執行函式配合閉包儲存當前索引值,你也可把它放在外面,不過思路是一樣
-
解除引用值( 既單獨為每個i取唯一識別符號 )
for (let index = 0; index < children.length; index++) { // var -> let children[index].onclick = function () { console.log(index) // -> 0, 1, 2 } }
最簡單的莫過於直接用ES6的 let 區域性變數 替換var, 還有一種解決方案
for (var index = 0; index < children.length; index++) { children[index].i = index // 給當前dom元素新增一個元素儲存索引 children[index].onclick = function(){ console.log(this.i) // -> 0, 1, 2 } }
一點拙見,如有不足,望相互探討