今天看到了一个很有趣的问题,关于闭包在内存中是否真的不会被回收,所以我也来写篇小短文谈谈我的理解。
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
函数。
回到开头的问题,答案就是:闭包对象并非不能被垃圾回收机制回收,具体仍然需要视情况而定。