for循环绑定事件解决方法

  首先我们来看一段代码

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oUl = document.getElementById("ul");
        var aLi = oUl.getElementsByTagName("li");
        for(var i = 0; i < aLi.length; i++){  
                    aLi[i].onclick = function (){  
                        alert(i);  
                    };
                }  
    </script>
</body>

  运行之后发现无论点哪个标签,弹出的都是最后一个标签的index

  这是因为 for 循环的里面 var 定义的变量 i 自动提升为全局变量,等同于下面的代码

<script type="text/javascript">
    var oUl = document.getElementById("ul");
    var aLi = oUl.getElementsByTagName("li");
    var i;
    for(i = 0; i < aLi.length; i++){  
        aLi[i].onclick = function (){  
            alert(i);  
        };
    }  
</script>  

  这时候 alert(i) 里面的i还没有值,当用户调用 onclick 的匿名函数时,需要对i求值

  解析程序首先会在事件处理程序内部查找,但 i 没有定义。然后,又到方法外部去查找,此时有定义,但此时的i已经循环完毕,因此,无论点哪个标签,弹出的都是最后一个标签的index。

  有以下几种方法解决:

  立即调用的函数表达式(IIFE);不懂的可以看看我之前写的  点击这里

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(var i = 0; i < ali.length; i++){
            (function(i){   //这里的i类似形参 
                ali[i].onclick = function (){  
                    alert(i);   
                } 
            })(i);   //这里的i类似实参
         
    }  
    </script>
</body>   

  将变量 i 保存给在每个段落对象(li)上

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(var i = 0; i < ali.length; i++){
            ali[i].i = i;
                ali[i].onclick = function (){  
                    alert(this.i);   
                } 
        }  
    </script>
</body> 

  将变量 i 保存在匿名函数自身

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(var i = 0; i < ali.length; i++){
            (ali[i].onclick = function (){  
                alert(arguments.callee.i);   
            }).i = i
        }
    </script>
</body> 

  还有一种使用ES6新语法 let 关键字 由于是新语法 各浏览器支持不同

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(let i = 0; i < ali.length; i++){
            ali[i].onclick = function (){  
                alert(i);   
            }
        }
    </script>
</body>

  关于let,我一直似懂非懂,后来在阮一峰老师的《ECMAScript 6 入门》这本书上找到了答案

  使用let,声明的变量仅在块级作用域内有效,当前的 i 只在本轮循环有效,所以每次都是一个新的变量

  可能你会问,如果每一轮循环的变量 i 都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值

  这是因为Javascript引擎内部会记住上一轮循环的值,初始化本轮的变量 i 时,就在上一轮循环的基础上进行计算。

  题外话

  for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部则是一个单独的子作用域

for (let i = 0; i < 3; i++) {
    let i = "abc" ;
    console.log(i);
}
 // abc
 // abc
 // abc

   上面的代码输出了3次 abc,这表明函数内部的变量 i 和外部的变量 i 是分离的

posted @ 2017-04-27 00:26  章洵  阅读(4912)  评论(1编辑  收藏  举报