一、内存泄露的概念
内存泄漏 是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
二、闭包造成内存泄漏的原因
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 —— 朋友,这就是闭包的效果。
var fn;
function foo() {
var a = 2;
function baz() {
consolel.log( a );
}
fn = baz; //将baz分配给全局变量
}
function bar() {
fn(); //妈妈快看呀,这就是闭包!
}
foo();
bar(); //2
过程解析:
函数 bar() 的词法作用域能够访问 foo() 的内部作用域。然后我们将bar()函数本身当做一个值类型进行传递。在这个例子中,我们将bar所引用的函数对象本身当做返回值。
在foo()执行后,其返回值(也就是内部的bar()函数)赋值给变量baz并调用baz(),实际上只是通过不同的标识符引用调用了内部的函数bar()
bar()显然可以被正常执行。但是在这个例子中,他在自己定义的词法作用域以外的地方执行。
在foo()执行后,通常会期待foo()的整个内部作用于都被销毁,因为我们知道引擎有垃圾回收器用来释放不再使用的内存空间。由于看上去foo()的内容不会再被使用所以很自然地会考虑对其进行回收。
而闭包的‘神奇’之处正是可以阻止这件事情的发生。事实上内部作用于依然存在,因此没有被回收。谁再使用这个内部作用域? 原来是bar()本身在使用。
拜bar()所声明的位置所赐,他拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存活,以供bar()在之后任何时间进行引用。
bar()依然持有对该作用域的引用,而这个引用就叫做闭包。
因此,在几微秒之后变量baz被实际调用(调用内部函数bar),不出意料他可以访问定义时的词法作用域,因此他也可以如预期般访问变量a.
例二 证明了无论通过何种手段将内部函数传递到所在的词法作用域以外,他都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用这个闭包。