当闭包遇上for
看看这个函数先:
function fun1(x) {
var a = x; //a为局部变量
function fun2() {
return a;
}
return fun2;
}
var fun3 = fun1(10);
var fun4 = fun1(9);
alert(fun4()); //9
alert(fun3()); //10
a = 99; //此时的a定义在全局环境下
alert(fun3()); //10
看出来没?fun3与fun4其实是通过闭包函数绑定了fun1的局部私有变量,同时fun3与fun4其实引用的闭包是不同的,但在内存合理分配的角度上看,更准确的说fun3与fun4的区别在于引用同一闭包函数时传入闭包函数的参数值是不同的,也就是说,我们可以通过参数来看函数的执行。
我们可以这样说:fun3是存储了a为10的引用的闭包调用,fun4是存储了a为9的引用的闭包调用。
所以,以下的输出就不难理解了:
window.onload = function(){
var oUl = document.getElementsByTagName("ul");
var aLi = oUl.getElementsByTagName("li");
var l = aLi.length;
for( i =l - 1 ; i >= 0 ; i--){
aLi[i].onclick = function (){
alert(i);
}
}
此时点击li的结果都是弹出aLi.length的值,假设aLi.length为4,分析如下~
原因是:js中,函数可通过调用而在不同时刻执行,时间由我们定,但对于for,if等流程控制语句其实是逐行进行的,也就是说当点击li触发onclick函数时,for循环已经结束,此时i值为4,所以点击时传入闭包的值为4,也就是说此时传入要调用的闭包的变量值为4,所以结果皆为4。我们之所以会有疑惑,大部分都是因为觉得传入的参数是aLi[i]中的i,其实此时传入的是for执行完时的 i 的引用;
我是这样想的,通过for给aLi标序并添加onclick存放参数值为 i 的闭包的引用,此时传入的 i 为全局变量,for执行结束,i 为4,所以要为各li 在局部锁定相应 i 值,才能输出想要的结果,这时就要利用到闭包的特性了。
看看怎么解决吧:
window.onload = function(){
var oUl = document.getElementsByTagName("ul");
var aLi = oUl.getElementsByTagName("li");
var l = aLi.length;
for( i =l - 1 ; i >= 0 ; i--){
aLi[i].onclick = function (a){
return function(){
alert(a);
}
}(i);
}
}
将aLi[i]的 i 当为参数传入内部函数,也就是说将 i 当做参数通过function (a)中的a传入return出来的闭包,此时alert(a);中的a与传入的 i 同值。实现了“绑定”,得到了想要的结果~
当然,问题的解决方法往往不止一个,分清楚希望传入闭包的变量是什么往往可以更快的找到解决方案。