记得之前曾经说过:对于闭包,普遍说法是“函数嵌套函数”,即可以调用外部环境变量的函数。的确因为闭包处于外层函数的执行环境中,所以可以取得外层函数作用域,这概念很明显,但不得不说,对于闭包与变量之间还老觉得有说不明道不清的感觉。
先看看在维基的眼中什么是闭包:在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
在计算机科学中,垃圾回收(英语:Garbage Collection,缩写为GC)是一种自动的存储器管理机制。当一个计算机上的动态存储器不再需要时,就应该予以释放,以让出存储器,这种存储器资源管理,称为垃圾回收(garbage collection)。
JS的垃圾回收机制是运行环境自动运行的,在早期的IE里,使用的是引用计数回收法,而Firefox,Chrome,后期的IE等浏览器使用的是标记法。
闭包之所以能使变量长期驻留在内存中就是因为对变量的引用,使得其引用次数不为零而不被销毁,而闭包被全局变量引用,所以也不会被销毁,则不主动释放变量,变量将常驻内存中。
现在发现下面的代码其实也挺可爱的:
(1).function fun1(x) {
a = x; //a为全局变量
function fun2() {
return a;
}
return fun2;
}
var fun3 = fun1(10);
var fun4 = fun1(9);
alert(fun4()); //9
alert(fun3()) ;//9
a = 99; //改变了全局环境下的a
alert(fun3()); //99
此时对a的赋值皆为对全局环境下a的改变,alert的a都在全局环境下搜寻到的,所以后赋值的覆盖前赋值的
(2).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
此函数中,a为局部变量,由于执行var fun3 = fun1(10);时,a=10被fun3在全局环境下引用,所以该a不被销毁,被作为局部变量保存下来。执行var fun4 = fun1(9);时,重新调用fun1,fun2函数声明提升并赋初始值a = 10,此时fun4会创建新的引用,a为10,与fun3的a互不干扰。而最后的a = 99;是定义在全局,执行alert(fun3());时,就近搜寻变量(涉及作用域链),所以获得局部环境的a值10;
在我看来,闭包之所以为闭包,就是我们机智的避开了垃圾回收机制,将所需的局部变量包裹(锁定)在闭包中丢到所需环境(隐式)使用,一般丢到全局环境。
但是,这是有隐患的,尤其经常发生在我们不知觉的情形下......
内存泄漏
在网页关闭但浏览器尚未关闭时,被关闭网页中有废弃变量存在于内存中没被垃圾回收机制处理掉时,就存在内存泄漏了。直观表现是,此时关闭所有网页而不关闭浏览器时的内存占用比刚刚开启浏览器而尚未打开任何网页多得多,当然这时的内存有一部分在于页面缓存与历史记录的占用;更直观的表现在于你的浏览器使用速度不断下降~
这是为什么呢?还是先看看闭包吧,由于闭包在全局被引用,而全局变量在网页开启时是不被释放的,也就是说闭包有个引用,那此时闭包对局部变量的引用也不会被释放,对于标记法来说,即该局部变量未离开环境,而计数法,则是引用不为0,此时,闭包有了一个属于自己的局部变量,而网页卸载是,全局环境被销毁,所有变量的占用内存被释放。
那引用计数法会怎样?在局部的循环引用会有弊端~
function () {
var a = [];
var b = [];
a.c = b; //b被a引用
b.d = a; //a被b引用
}
我的理解是这样的,函数调用结束后,a由于被引用了,所以不被销毁,此时若是b被销毁那就相安无事了,可是坑爹的,b此时也被a引用了,结果a,b在谁先被销毁的问题上吵起来了,站在一旁的垃圾回收者左右为难,最后干脆就不理了,甩头就走,所以a,b掉队,木有了组织的领导~
假设内存泄漏量已占用过多内存,估计就卡机了~
在IE中,JS对象使用的是标记法清除,而DOM,BOM元素使用的却是计数法,这就是问题所在了。