window.onload = function() {
var a = function(x) {
console.log(x);
};
document.onclick = function() {
a(1);
};
document.onkeyup = function() {
a(2);
};
};
我很清楚为什么会这样,但不是如何。
当window.onload
结束时,a
被销毁,但它引用的函数仍可供事件处理程序使用,因为它是在更高的范围内声明的,如果我正确理解这一点。
解释器是否在后台保留了对该函数的隐藏引用,或者解释器是否以某种方式内联该函数?这种类型的代码是否避免全局变量有效?感谢。
答案 0 :(得分:1)
当
window.onload
结束时,a
会被销毁......
这个假设是错误的。无论何时在另一个函数中创建函数,这些内部函数都会阻止垃圾收集器清除外部函数的范围。
推荐阅读:http://www.ibm.com/developerworks/web/library/wa-memleak/
答案 1 :(得分:0)
这被称为“关闭”。简而言之,它意味着“函数可以访问定义它的范围内的所有变量”。
在您的示例中,两个处理程序在window.onload
函数内定义,因此它可以访问a
,这也在该范围内。
是的,解释器将所有闭包保留在内存中。 (AFAIK)
我不是一个专业的程序员,所以我没有太大的权威。但我通常这样做是为了避免使用全局变量。
答案 2 :(得分:0)
这不是对您的问题的完整答案,但如果它有帮助... Javascript中的对象生命周期通常通过引用计数来控制。
该语言实际上并没有块范围的概念(尽管它确实享有功能范围)。只有在没有引用的情况下,才会由后台GC自动清理对象。因此,对象a
的行为本质上是“安全的”:当函数作用域结束时,一个引用被删除,但另一个引用仍然存在。
更详细的答案必须错综复杂地探索特定的Javascript引擎的实现,可能比我们在这里有更多的时间或空间。
答案 3 :(得分:0)
在javascript中的变量中对它们的函数或引用是一种对象,就像常规对象一样,在没有对它们的引用之前,不会对包含对它们的引用的函数或变量进行垃圾收集。在您的特定情况下,您创建了一些对a()
的引用,其持续时间超过.onload()
处理程序。因此,变量a
的持续时间也比函数长得多。这个概念被称为“封闭”。
如果您在Google中搜索“javascript中的闭包”这一术语,您会发现许多有关此主题的有用文章,例如this one。
答案 4 :(得分:0)
解释器是否在后台保留对该函数的隐藏引用,或者解释器是否以某种方式内联函数?
对于所有javascript引擎,无法全局回答。但是,对于您在a
和document.onclick
上定义的匿名函数,解析器很可能必须保留对函数document.onkeyup
的引用。所以a
不会立即被破坏,只能通过javascript变量范围的限制来访问它。
这就是闭包可能导致内存泄漏的原因,因为引擎很难正确地解除引用a
。 (这是IE engine的一个问题,仍然是在没有仔细处理的情况下)。由于您无法手动取消引用a
,因为您无权访问该变量,因此无法快速修复此类模式引起的内存泄漏。
你的例子也是而不是一个closure,因为它没有返回一个函数,但它处理同一种变量范围问题,因为它引用了一个局部变量{{ 1}}内部函数不共享相同的范围(a
和document.onclick
)。
我非常怀疑没有引擎会尝试内联函数document.onkeyup
,因为这会不必要地复制函数对象。它很可能保留pointer
,并且一旦引用计数降至0,就可以轻松销毁所提及的引用计数(在您的示例中,a
和document.onclick
时取消引用(设置为document.onkeyup
或null
)。
这种类型的代码是否能避免全局变量的效率?
避免全局变量总是好的,所以一般来说:是的,它是避免全局变量的有效方法。一旦undefined
和a
被解除引用,每个好的引擎也应该销毁document.onclick
。
答案 5 :(得分:0)
当您在window.onload
处理程序中声明第一个函数时,JavaScript会自动创建作用域或上下文对象;此范围对象保留变量a
,有效地使其成为两个匿名函数的唯一。
此技术通常用于隐藏信息并防止必须创建全局变量。运行onload
代码后,其他任何代码都无法访问a
。