js闭包-----浅谈
接触js也有一段时间了,最近看了一些关于闭包的问题,不是很理解,就决定花一点时间去弄懂它。于是乎在网上查阅了大量的资料,看了很多例子。接下来简单的讲一下我对闭包的理解。
首先我们学一个知识点之前,肯定要思考的一个问题就是我们为什么要去学它?它到底有什么用?能解决什么问题?
什么是闭包?
“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
相信很少有人能直接看懂这句话,因为他描述的太学术。我想用如何在Javascript中创建一个
闭包
来告诉你什么是闭包,因为跳过闭包的创建过程直接理解闭包的定义是非常困难的。看下面这段代码:
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
c();
这段代码,有两个特点:
1.在函数a里面嵌套了一个函数b
2.函数a返回了函数b
这样就是一个简单的闭包。
a函数里面定义了一个局部变量i ,因为这是一个局部变量,一般会在函数执行完毕之后,被销毁,但是由于i在函数b中被引用,
而函数b又被return,被赋值给了全局变量c。所以当函数a执行完之后,i不会被销毁。
你可以在上面的代码下继续执行函数c就会弹出2,3,4,5.因为此时变量i依然存在而且保留着上一次执行完之后的数据。
闭包有什么用?
相信看了上面的一个例子,我们就能知道得到这个答案。
闭包能在函数外部,访问函数的局部变量,能使变量始终存在内存中。
再以一个简单的例子说明一下:
var ulo = document.getElementById('ulo');
var lio = ul.getElementsByTagName('li');
for(var i = 0;i<10;i++){
lio.addEventListener('click',function(){
alert(i);
})
}
假设html为:
<ul id="ido">
<li></li>
......
</ul>
上面的js代码为10个li绑定了事件,但是真正执行时,会发现无论我点哪一个li结果都会alert(9);
因为当我们点击li时这段代码已经执行完一遍了,此时的i已经是9了,所以当我们点击任何一个li都会弹出9。
解决方法:
var ulo = document.getElementById('ulo');
var lio = ulo.getElementsByTagName('li');
for(var i = 0;i<10;i++){
!function(i){
lio.addEventListener('click',function(){
alert(i)
})
}(i)
}
此时的i不会去向外部的i去获取(因为此时外部的i值已经是执行完之后的值了)。而是通过闭包保存该i值,这个值就是当时的i值,当点击时,会向即时函数获取i值,也就是被保存起来的那个i值。
js的垃圾回收机制
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。所以闭包的使用要慎重,要在不使用时,释放变量,防止内存泄漏。
好了,这就是我现阶段对于闭包的理解,可能还有些偏差,也不是很全面,不喜勿喷,我也会在之后做相应的更新。希望能对一些小伙伴有所帮助。
下面的是补充:
前面讲的可能还是有有点笼统,后来我又去查了一些文档,再来稍微详细的讲一下,那个例子:
var arr = [] for(var i = 0;i<10;i++){ arr[i] = function(){ alert(i) } }
这样的代码,在执行arr1-9时输出的都是10。
这么理解呢?
首先在全局作用域中会有变量arr,和变量i
然后·执行for循环,给arr赋值,这时的程序不会去看function里面的东西,因为这只是一个赋值,但是此时的arr[i]的作用域链已经确定了,就是GO全局作用域
然而当执行的时候alert(i) 这句语句时,会去arr[i]的作用域中找变量 i 此时的i已经是10了,所以输出10.
怎么解决这个问题?
var arr = []
for(var i = 0;i<10;i++){
(function(j){
arr[j] = function(){
alert(j)
}
}(i))
}
这样写有什么好处呢?
其实是在每次赋值时,使用立即执行函数,形成了新的作用域并且把每次循环的i都保存了下来,此时在进行赋值操作时的arr[i]的作用域链,就不止全局作用域了,还有立即执行的那个函数的作用域,在那个作用域中保存了i也就是形参j,所以在每次执行时,都能取到被保存的值。