这一篇博客承接上一篇,如果大家没看上一篇,建议看看.....直通车..... 好吧,咱们一起来看看这个闭包,这次我们的重点并不是弄明白闭包是什么?而是搞清楚JS的闭包是怎么产生的。接着上一篇博客的示例:
var a = function(x){ var b = 'bb'; var inner = function(){ var c = 'cc'; }; return b; };
当a函数执行到给inner变量赋值匿名函数之后,形成下面的引用关系,直接复用上次博客的图:
从上图很容易看出,这时候inner函数对象的[[Scope]]内部属性引用着函数a在执行期的活动对象(这里包含了a函数的全部局部变量)和全局作用域。如果这时候我们把这个inner函数传递出去,如下代码:
var a = function(x){ var b = 'bb'; var inner = function(){ var c = 'cc'; }; return inner ; }; var closure = a();
这段代码意思是:执行a函数,把inner函数返回,并把它赋值给closure变量,这时候全局变量closure和之前的局部变量inner引用同一个函数,大家可以运行下面的代码:
var a = function(x){ var b = 'bb'; var inner = function(){ var c = 'cc'; console.log(inner === closure); }; return inner ; }; var closure = a(); closure();/*输出控制台:true*/
这段代码证实了之前所言变量closure和inner引用同一个函数对象,换句话说就是,名为inner的函数和名为closure的函数是同一个函数,closure函数的作用域链和inner函数的当然也是一样一样的,如下图:
这时候再结合上一篇博客,大家该明白了,为什么当a函数返回后,closure函数还可以使用a函数内部的局部变量了吧。
这时候还有一个问题,那就是类比其他语言时,例如C语言,当一个函数执行完毕返回后,应该释放所有这个函数执行期所占用的内存资源,当然也包含它的局部变量了,如果不这么做,不是会造成内存泄露么?JS解释器当然会释放,但是JS解释器是这样做的:当函数执行完毕后,由JS解释器的垃圾回收机制来释放内存,它的工作机制大体上是这样的:它会定期执行,判断一块被占用的内存区域是否还被可用变量所引用,如果没有,直接释放内存,如果有,那就不释放。其实我们在编写JS代码时是没有办法直接销毁对象所占用的内存空间的,内存的释放基本依赖JS的垃圾回收机制。那么有人会说delete运算符,其实delete运算符也无法销毁对象所占用的内存空间,如下示例:
var a = {k:'占用内存'}; var b = a; delete a; console.log(b);/*{k:'占用内存'}对象并未被释放*/ console.log(a);/*语法错误,a is not defined*/
那么delete到底做了什么呢?它销毁了a变量,这样原来变量a和b都引用{k:'占用内存'}对象所占的内存区域,现在销毁a后,就只有b引用这块内存区域了。如果我们再执行下一条运算:
delete b;
这时候,就没有任何可用变量引用{k:'占用内存'}对象所占的内存区域了,垃圾回收机制自然会识别并释放资源。
回到闭包的话题上来,如果不产生闭包的话,a函数执行完后。a函数的执行期上下文自然会被垃圾回收机制所识别释放,a函数内部的的局部变量inner函数对象也会被释放掉,这点毫无疑问,不会留下任何内存泄露的问题。但是,如果将inner函数对象作为结果返回并赋值给了closure,产生了闭包,那么这个函数对象将不会被销毁,它的内部变量[[Scope]]引用的作用域链当然也就不会被销毁。一切都这么自然而然的发生了........^_^
不过JS的闭包,如果使用不当,是很容易造成内存泄露的,但是鉴于它的好处,我们既要使用它,也要避免造成内存泄露,所以深刻的理解它很重要^_^
好吧,今天就这些。如有错误,请轻拍............