<ul> <li>第一个</li> <li>第二个</li> <li>第三个</li> <li>第四个</li> </ul> var nodes = document.getElementsByTagName("li"); for(i = 0,len=nodes.length;i<len;i++){ nodes[i].onclick = function(){ alert(i); //值全是4 }; }
事与愿违,无论点击哪一个li,都是alert(4),也就是都是alert循环结束之后的索引值。这是为什么呢?
这是因为循环中为不同的元素绑定事件,事件回调函数里如果调用了跟循环相关的变量,则这个变量取循环的最后一个值。
由于绑定的回调函数是一个匿名函数,所以上面的代码中, 这个匿名函数是一个闭包,携带的作用域为外层作用域(也就是for里面的作用域),当事件触发的时候,作用域中的变量已经随着循环走到最后了。
还有一点就是,事件是需要触发的,而绝大多数情况下,触发的时候循环已经结束了,所以循环相关的变量就是最后一次的取值。
要实现点击li,alert出li的索引值,需要将上面的代码进行以下的修改:
<ul> <li>第一个</li> <li>第二个</li> <li>第三个</li> <li>第四个</li> </ul> var nodes=document.getElementsByTagName("li"); for(var i=0;i<nodes.length;i++){ (function(e){ nodes[i].onclick=function(){ alert(e); }; })(i) }
解决思路: 增加若干个对应的闭包域空间(这里采用的是匿名函数),专门用来存储原先需要引用的内容(下标)。
当立即执行函数执行的时候,e 值不会被销毁,因为它的里面有个匿名函数(也可以说是因为闭包的存在,所以变量不会被销毁)。执行后,e 值 与全局变量 i 的联系就切断了,
也就是说,执行的时候,传进的 i 是多少,立即执行函数的 e 就是多少,但是 e 值不会消失,因为匿名函数的存在。
也可以用下面的解法,原理是一样的:
<ul> <li>第一个</li> <li>第二个</li> <li>第三个</li> <li>第四个</li> </ul> var nodes=document.getElementsByTagName('li'); for(var i = 0; i<nodes.length;i++){ (function(){ var temp = i; nodes[i].onclick = function () { alert(temp); } })(); }
注意事项
1、造成内存泄露
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,所以只有在绝对必要时再考虑使用闭包。
2、在闭包中使用this也可能会导致一些问题。
代码示例:来源于《js高级程序设计3》;
其实我们的目的是想alert出object里面的name
var; var object={ name:"My Object", getNameFunc:function(){ return function(){ return this.name; } } } alert(object.getNameFunc()()); // The Window
因为在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。
每个函数在被调用时,都会自动取的两个特殊变量:this和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止。也就是说,里面的return function只会搜索
到全局的this就停止继续搜索了。因为它永远不可能直接访问外部函数中的这两个变量。
稍作修改,把外部作用域中的this对象保存在一个闭包能够访问的变量里。这样就可以让闭包访问该对象了。
var; var object={ name:"My Object", getNameFunc:function(){ var that=this; return function(){ return that.name; } } } alert(object.getNameFunc()()); // My Object
我们把this对象赋值给了that变量。定义了闭包之后闭包也可以访问这个变量。因此,即使在函数返回之后,that也仍引用这object,所以调用object.getNameFunc()()就返回 “My Object”了。
总结
当在函数内部定义了其他函数,就创建了闭包。闭包有权访问包含函数内部的所有变量。
闭包的作用域包含着它自己的作用域、包含函数的作用域和全局作用域。
当函数返回一个闭包时,这个函数的作用域会一直在内存中保存到闭包不存在为止。
使用闭包必须维护额外的作用域,所有过度使用它们可能会占用大量的内存