0%

闭包在内存中真的不会被回收吗

今天看到了一个很有趣的问题,关于闭包在内存中是否真的不会被回收,所以我也来写篇小短文谈谈我的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getCounter() {
let a = 1;

function counter() {
a = a + 1;
return a;
}
return counter;
}

console.log(getCounter()());
console.log(getCounter()());
console.log(getCounter()());

先看一段代码。

众所周知这道题的答案是

1
2
3
2
3
4

然而其实不是!!!

这道题的正确答案是

1
2
3
2
2
2

的确,在 getCounter 函数中创建了函数 counter,而且在函数 counter 中也的的确确访问了 getCounter 中的变量 a。根据经验,此处一定会形成闭包毋庸置疑,因为 getCounter 的词法作用域被它内部的函数 counter 的词法作用域引用了。

我们也可以根据控制台的打印结果看出来这里确实形成了闭包。

问题在于执行上下文

从闭包的具体实现上来说,对于函数 counter 而言,闭包对象 Closure (getCounter) 存在于自身的 [[Scopes]] 属性中。也就是说,只要函数体 getCounter 在内存中持久存在,闭包就会持久存在。而如果函数体被回收,闭包对象同样会被回收。

在预解析阶段,函数声明会创建一个函数体,并在代码中持久存在。但是并非所有的函数体都能够持久存在。在上面的示例中,counter 函数是在 getCounter 函数的执行上下文中声明的,当执行上下文执行完毕,执行上下文就会被回收,那么在 getCounter 执行上下文中声明的 counter 函数也会被回收。所以显然由 countergetCounter 产生的闭包也会被回收,我们每次执行 getCounter()(),实际上创建了不同的闭包对象。

而我们平常见到的闭包版本,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getCounter() {
let a = 1;

function counter() {
a = a + 1;
return a;
}
return counter;
}

const myCounter = getCounter();
console.log(myCounter());
console.log(myCounter());
console.log(myCounter());

显然对于函数 counter 的引用会一直保存在内存中,所以我们总能访问到 counter 函数。

回到开头的问题,答案就是:闭包对象并非不能被垃圾回收机制回收,具体仍然需要视情况而定。

本文标题:闭包在内存中真的不会被回收吗

文章作者:Flower-F

发布时间:2022年03月11日 - 20:31

最后更新:2022年03月11日 - 21:00

-------------本文结束,感谢您的阅读-------------

欢迎关注我的其它发布渠道