1.简单的例子
首先从一个经典错误谈起,页面上有若干个div, 我们想给它们绑定一个onclick方法,于是有了下面的代码
<ul id="divTest"> <li>0</li> <li>1</li> <li>2</li> <li>3</li> </ul> <ul id="divTest2"> <li>0</li> <li>1</li> <li>2</li> <li>3</li> </ul>
初次实现:
var div = document.getElementById("divTest"); var spans = div.getElementsByTagName("li"); for (var i = 0; i < spans.length; i++) { spans[i].onclick = function () { console.log(i); } }
结果:显示都是4,不是预期结果,想要的0,1,2,3,
进行如下修改,得到预期效果
var div2 = document.getElementById("divTest2"); var spans2 = div2.getElementsByTagName("li"); for (var i = 0; i < spans2.length; i++) { (function (num) { spans2[i].onclick = function () { console.log(num); } })(i); }
2.究其缘由
第一种方式,在页面加载后就会执行,当i的值为4的时候,判断条件不成立,for循环执行完毕,但是因为每个li的onclick方法这时候为内部函数,所以i被闭包引用,内存不能被销毁,i的值会一直保持4,直到程序改变它或者所有的onclick函数销毁(主动把函数赋为null或者页面卸载)时才会被回收。
这样每次我们点击li的时候,onclick函数会查找i的值(作用域链是引用方式),一查等于4,然后就显示给我们了。
而第二种方式是使用了一个立即执行的函数又创建了一层闭包,函数声明放在括号内就变成了表达式,后面再加上括号括号就是调用了,这时候把i当参数传入,函数立即执行,num保存每次i的值。