今天看到了一个很有趣的问题,关于闭包在内存中是否真的不会被回收,所以我也来写篇小短文谈谈我的理解。
1 | function getCounter() { |
先看一段代码。
众所周知这道题的答案是
1 | 2 |
然而其实不是!!!
这道题的正确答案是
1 | 2 |
的确,在 getCounter 函数中创建了函数 counter,而且在函数 counter 中也的的确确访问了 getCounter 中的变量 a。根据经验,此处一定会形成闭包毋庸置疑,因为 getCounter 的词法作用域被它内部的函数 counter 的词法作用域引用了。
我们也可以根据控制台的打印结果看出来这里确实形成了闭包。

问题在于执行上下文。
从闭包的具体实现上来说,对于函数 counter 而言,闭包对象 Closure (getCounter) 存在于自身的 [[Scopes]] 属性中。也就是说,只要函数体 getCounter 在内存中持久存在,闭包就会持久存在。而如果函数体被回收,闭包对象同样会被回收。
在预解析阶段,函数声明会创建一个函数体,并在代码中持久存在。但是并非所有的函数体都能够持久存在。在上面的示例中,counter 函数是在 getCounter 函数的执行上下文中声明的,当执行上下文执行完毕,执行上下文就会被回收,那么在 getCounter 执行上下文中声明的 counter 函数也会被回收。所以显然由 counter 与 getCounter 产生的闭包也会被回收,我们每次执行 getCounter()(),实际上创建了不同的闭包对象。
而我们平常见到的闭包版本,如下:
1 | function getCounter() { |
显然对于函数 counter 的引用会一直保存在内存中,所以我们总能访问到 counter 函数。
回到开头的问题,答案就是:闭包对象并非不能被垃圾回收机制回收,具体仍然需要视情况而定。