JavaScript 闭包是怎么通过作用域链霸占更多内存的?
本文是作者学习《JavaScript 高级程序设计》7.2第一小节的一点个人理解,详细教程请参考原教材。
在弄清楚这个问题之前,我们有必要搞清楚下面这几个问题:
首先,什么是闭包?
定义:闭包 是指 有权访问另一个函数作用域中变量的函数。
怎么创建闭包?
在一个函数内部创建另一个函数,是创建闭包最常见的方式。
一个一般的函数执行的时候都发生了什么?
创建一个执行环境,里面有这个函数的变量对象,保存了函数可访问的所有数据。
创建了作用域链,用来保证可访问变量的有序访问。
函数执行过程创建了一个函数的活动对象,作用域链的最前端指向这个对象。
函数读取写入值,在作用域链里面查找。
函数执行完毕返回值后:执行环境、作用域链和活动对象一并销毁。
理解了这些之后,考察下原书的代码:
function createCompare(property){
return function(obj1,obj2){
var value1=obj1.property;
var value2=obj2.property;
if (value2 < value1){
return 1;
} else if (value2 >value1){
return 1;
} else {
return 0;
}
};
}
var compareNames = createCompare("name");
var result = compareNames({name : "mars1"},{name : "mars2"});
其中,createCompare 函数返回了一个匿名函数,这个匿名函数就是一个闭包。它引用了createCompare函数的参数变量property。
创建compareNames 变量的时候,就创建了createCompare函数的活动变量:其中property = name etc. 同时返回了匿名函数给compareNames变量,它也有自己的执行环境与作用域链,作用域链引用了创建的compareNames活动对象。
result 定义过程,创建了compareNames的活动对象,其中obj1 = {name : "mars1"} etc.
这样,为result 赋值的compareNames函数执行环境的作用域链如下:
compareNames活动对象 → createCompare活动对象 → 全局对象。
那么问题来了,这个闭包是怎么导致占用更多内存的?
函数执行完毕,compareNames 变量并未消失,不会被垃圾回收,而它指向的闭包(匿名函数)的作用域链引用着createCompare 函数的活动变量。所以createCompare函数虽然执行完毕返回了匿名函数,但是它的活动对象并不会和执行环境、作用域链一样被销毁,因为还有compareNames的作用域链在引用。这样就相当于闭包携带了包含它的函数活动对象。
因此要释放compareNames携带的作用域,必须手动解除引用:
var compareNames = null;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。