js闭包问题

在看js闭包这一块儿,有一个地方难以理解。代码如下。为什么打印出来的始终是6呢

<script>
    window.onload = function(){
        var ul =document.getElementsByTagName('ul')[0];
        var li = ul.getElementsByTagName('li');
        for(var i=0;i<li.length;i++){
                li[i].onclick = function(){
                console.log(i);
            }
        }
    }
</script>
<body>
<ul>
    <li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li>
</ul>

</body>
阅读 3k
6 个回答

老有人问这种文问题,当然我刚学的时候,也来社区问过这个问题。简单说下,你那个绑定的点击事件函数,是当你点击了之后才会出触发,进入那个函数执行相应的代码的,所以一开始那个 i=1,2,3... 根本就没到那个函数里面去,你只是每次都给li 绑定了个 function(){console.log(i)};这个i并没有值。然后等你 去点击触发,这个时候i已经循环了一遍了,最后i就变成了 6,最后 进去执行那个函数 的时候 打印的那个i 当然就是 6咯。

因为i最终的值是6,6个li的事件回调函数里面是共享的同一个i,可以这样解决:

window.onload = function(){
        var ul =document.getElementsByTagName('ul')[0];
        var li = ul.getElementsByTagName('li');
        for(let i=0;i<li.length;i++){
                li[i].onclick = function(){
                console.log(i);
            }
        }
    }

或者

window.onload = function(){
        var ul =document.getElementsByTagName('ul')[0];
        var li = ul.getElementsByTagName('li');
        for(var i=0;i<li.length;i++){
                li[i].onclick = (function(i){
                    return function(){
                        console.log(i);
                    }
                })(i);
        }
    }

window.onload = function(){

    var ul =document.getElementsByTagName('ul')[0];
    var li = ul.getElementsByTagName('li');
    for(var i=0;i<li.length;i++){
            li[i].onclick = function(){
            console.log(i);
        }
    }
}

只有最外面的var i = 0;的位置定义了i。里面console.log()的时候用的也是var定义的这个i

要看作用域,在js中,对于var是没有块作用域的概念的,当你的for循环执行完毕之后,i最后的值为6。
可以用高级关键字let来替代var

因为onclick绑定的匿名函数是闭包,并且不是立即执行的。只有在触发click事件的时候才会执行,这时候这个闭包才会向window.onload对应的活动对象内取得i的值,而此时进行取值,已经是i存储在window.onload对应的活动对象里的最终值了,就是6.
如果想要获得预期的效果,你可以让这个闭包立刻执行,就可以取得当前值,比如这样:

    window.onload = function(){
        var ul =document.getElementsByTagName('ul')[0];
        var li = ul.getElementsByTagName('li');
        for(var i=0;i<li.length;i++){
            li[i].onclick = function(index){
                return function(){
                    console.log(index);
                }
            }(i);
        }
    }

详细的解答可以参考:《javascript高级程序设计(第3版)》 - P181

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题