闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:
1)作用域链
2)垃圾(内存)回收机制
3)函数嵌套...等等.
首先理解一下作用域链的含义,简单来说,作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引。
而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止。
当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续。
如果找到最后也没找到需要的变量,则解释器返回undefined。
然后再来看看js的内存回收机制。
一般来说,一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了。
对应的内存空间也就被回收了。下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用。但是
如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的。并且这个内部函数又使用了外部函数的某些变量的话
这种内存回收机制就会出现问题。
如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了。
所以js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来。
也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包。
而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收。也就是说,有了闭包,嵌套的函数结构才可以运作,这也是符合我们的预期的。
/** * Created by XBY on 2015/5/16. *模块化代码 */ var a = (function(){ var x = 1; //x是局部的变量 function bb(){ //私有的方法 x++; alert(x); } function cc(){ //私有的方法 x = x + 2; alert(x); } return { b : bb, c : cc } })(); a.b(); //2 a.c(); //4
闭包的好处:
1.希望一个变量长期驻扎在内存中; 2.避免全局变量的污染; 3.私有成员的存在闭包的用法: 1.模块化代码; 2.在循环中直接找到对应元素的索引。 没有利用闭包时:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>闭包</title> <script src="bibao.js"></script> </head> <body> <ul> <li>我是零</li> <li>我是一</li> <li>我是二</li> </ul> </body> </html>
window.onload = function(){ var aLi = document.getElementsByTagName("li"); for(var i = 0; i < aLi.length;i++){ aLi[i].onclick = function() { alert(i); //3 3 3 }; } }
利用了闭包时:
window.onload = function(){ var aLi = document.getElementsByTagName("li"); for(var i = 0; i < aLi.length;i++){ (function(i){ aLi[i].onclick = function() { alert(i); //0 1 2 }; })(i) } }
或者(另一种方式):
window.onload = function(){ var aLi = document.getElementsByTagName("li"); for(var i = 0; i < aLi.length;i++){ aLi[i].onclick = (function(i){ return function(){ alert(i); // 0 1 2 } })(i); } }
使用闭包时需要注意的问题:
IE会出现内存泄露:
window.onload = function(){ var oDiv = document.getElementById("div"); oDiv.onclick = function () { alert(oDiv.id); } }
元素内外部相互引用,就会出现。
解决办法1:
window.onload = function(){ var oDiv = document.getElementById("div"); oDiv.onclick = function () { alert(oDiv.id); } window.onunload = function () { oDiv.onclick = null; } }
解决办法2:
window.onload = function(){ var oDiv = document.getElementById("div"); var id = oDiv.id; oDiv.onclick = function () { alert(oDiv.id); } oDiv = null; }