JavaScript匿名函数是否会阻止垃圾回收器销毁变量?
问题:变量是否会被垃圾回收?
代码示例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> </head> <body> <button id="my-button">Do Something</button> <script type="text/javascript"> let setButtonFunc; setButtonFunc = function() { let button = document.getElementById("my-button"); let phrase = "Printing Something"; button.onclick = function() { text = document.createTextNode(phrase); document.body.append(text); } } setButtonFunc(); setButtonFunc = null; </script> </body> </html>
问题描述
我用let定义了setButtonFunc变量并赋值为一个函数,这个函数用来给按钮设置onclick事件,点击按钮时会往页面追加文本。
我的疑问是:把setButtonFunc设为null之后,phrase和button这两个变量会被垃圾回收器回收吗?
从逻辑上看,它们的引用好像都消失了,应该只有按钮的onclick函数留在内存里,其他变量因为没有外部引用会被回收。但实际点击按钮时,onclick函数还能正常执行,就像phrase从来没被释放过一样。
我搞不懂这点,比如在Python里做类似的操作,垃圾回收器会销毁变量并抛出异常。为什么JavaScript不会出现这种情况?是不是按钮的onclick匿名函数持有这些变量的引用?我是不是漏看了let的文档内容?
答案
phrase不会被垃圾回收,button会被回收- 核心原因是JavaScript的闭包机制:你赋值给
button.onclick的匿名函数,会捕获它所在作用域(也就是setButtonFunc函数的作用域)里的phrase变量。哪怕外层函数setButtonFunc已经执行完毕,这个匿名函数依然持有对phrase的引用,所以垃圾回收器不会回收phrase。 - 至于
button变量:因为匿名函数里并没有用到它,所以这个变量不会被闭包捕获。当setButtonFunc执行完、setButtonFunc本身被设为null后,button就没有任何引用了,会被垃圾回收器回收。 - 和Python的差异:Python也有闭包,但如果你的Python示例内层函数没有引用外层变量,或者外层变量未被持续持有,才会被回收。你觉得Python会抛出异常,大概率是因为你的Python示例中,内层函数尝试引用了已被销毁的变量,但在这个JS场景里,内层函数明确引用了
phrase,闭包会把这个变量保留下来。 - 关于
let:你没漏看关键内容,let只是定义块级作用域的变量,闭包的规则不受let或var的影响(两者仅作用域范围不同),只要内层函数引用了外层作用域的变量,这个变量就会被闭包保留,不会被垃圾回收。
内容的提问来源于stack exchange,提问作者Carl HR
相关产品推荐
相关产品推荐

