闭包中外层函数未被直接引用的变量何时被 GC 回收
举一个简单的闭包例子:
function A() {
var i = 1;
var j = 2;
return function(){
return i;
}();
}
var B = A();
在语句 return i
这一行设置断点,调试如下:
之前看过有关闭包的资料时知道由于函数 A
中的变量被引用,所以函数 A
不会被 GC
回收,那么这个不回收指的是整个函数 A
都不会被回收,还是被直接引用的部分不会被回收呢?
再举一例:
function A() {
var i = {x : 1};
var j = i;
return function(){
return j;
}();
}
var B = A();
此时,i
被 j
引用,所以 i
和 j
指向同一个内存空间,但是断点调试时依然访问不到未被直接引用的变量 i
:
我的猜想是,当我设置断点进行调试时由于是全局作用域,所以我访问不到外层函数内部未被闭包暴露在全局作用域下的变量,而并不是因为该变量已经被 GC
回收了。
那么我最终的问题是:未被闭包直接引用的外层函数的变量在我设断点调试时到底有没有被 GC
回收呢?如果未被回收,为何我调试时访问不到呢?是因为作用域的问题吗?如果此时未被回收,那么该变量何时会被回收呢?
Firefox
和 Safari
调试时居然是可以访问到未引用的变量 j
的,截图如下:
1.Firefox
:
2.Safari
:
现在基本明白了,可能是 debugger 的问题?
测试代码如下:
var v0 = 'i am at level 0';
var f1 = function () {
var v1 = 'i am at level 1';
var f2 = function (){
var i = 1;
var j = 2;
var f3 = function (){
console.log(i);
}
f3();
}
f2();
}
f1();
Chorme
:
Firefox
:
Safari
:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
注意右侧的方框,你为什么访问不到
j
?因为你的断点在
function(){...}();
的作用域里,而不是你认为的全局环境所以你可以访问到自身的
变量abc
(我为了举例加上的),闭包中的a
(你问题中的变量i
)访问不着
j
因为你没使用它,Closure里就没有它啊至于GC机制是不是立刻回收的,我不了解就不说了..
撸主两个问题,我答第二个吧,什么时候回收, 这个很有意思
A closure is a function that “captures” values near where it was born
closure 是在生成是“捕获”他周围值的函数。
所以,捕获的意思是 cache,还是 reference 呢:
例1:值?
好像 捕获 的是值啊?慢着
例2:也好像是引用啊?
所以,可以理解为捕获的是 reference 吗?有意思的来了
将 param 附一个新对象,意思旧的传入 add 的对象没有 ref 是不是会回收呢?看一下输出
还是1,惊呆了,不是报错,也不是新对象的值,说明 closure 也有一个最初传入 add 函数的参数引用,那个参数的对象还在内存中,而且现在只有 closure 引用他,因此不会被回收,这里也不会报错。
结论
closure “捕获”意思是,引用实际对象,也就closure中的
aNumber
实际上引到参数对象/值,所以改变传入的 param 引用不会影响 closure 的引用。因此只有 closure 没有引用时例子里则是addByEleven=null
,才会释放.据我以前的观察应该是debugger的scope问题(调试node的时候经常被这个坑),释放与否和函数体内是否有引用是没有关系的,实际上直到实际运行时,判断一个函数体究竟引用了哪些变量是不知道的(万恶的
eval
和with
,还有串联的引用function a => function b => variable c的情况)。所以ECMA标准的做法是不分析函数体内的代码,直接引用整个环境。ECMA相关标准 http://ecma-international.org/ecma-262/5.1/#sec-13 有空的话写篇导读吧
PS:不过不排除存在或以后可能存在某种激进的优化策略会试图分析这类情况予以回收释放