1. 程式人生 > >【JS】關於for迴圈中的click回撥函式索引值錯誤的思考

【JS】關於for迴圈中的click回撥函式索引值錯誤的思考

<!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; // 模擬自增

所述綜上由此可得出解決方案

  1. 當前值不可變( 既立即執行 )
    var index; // for迴圈中的index值
    index = 0; 
    setTimeout(function(){ // 模擬click點選事件
       console.log(index) // -> 0, 1, 2
    }, 1000) 
    ++index; //自增
     

    立即執行函式配合閉包儲存當前索引值,你也可把它放在外面,不過思路是一樣

  2. 解除引用值( 既單獨為每個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
        }
     }

    一點拙見,如有不足,望相互探討