请考虑the Mozilla Docs on JavaScript memory leaks中的这句话:
function addHandler() { var el = document.getElementById('el'); el.onclick = function() { this.style.backgroundColor = 'red'; } }
上面的代码将元素设置为在单击时变为红色。它 还会造成内存泄漏。为什么?因为对el的引用是 无意中陷入了为匿名内心创造的封闭 功能。这将在JavaScript之间创建循环引用 对象(函数)和本机对象(el)。
请以简单明了的方式解释上述泄漏的原因,我没有明确指出。
由于泄漏,网站/网页是否面临安全问题?我该如何避免它们?其他什么代码会导致内存泄漏?如何判断内存泄漏的时间?
我是内存泄漏主题的绝对新手。有人可以一步一步地为我澄清这些东西吗?也有人可以帮我澄清这句话“这会在JavaScript对象(函数)和本机对象(el)之间创建一个循环引用。”
答案 0 :(得分:19)
有两个概念可以帮助您理解这个例子。
1)闭包
闭包的定义是 Every inner function enjoys access to its parent's function variables and parameters.
当addHandler()
函数完成时,匿名函数仍然可以访问父变量el
。
2)功能=记忆
每次定义function
时,都会创建一个新对象。
是什么让这个例子有点混乱,onclick是一个只能设置为DOM元素一次的事件。
所以el.onclick = function(){};
肯定会覆盖旧功能吗?
错误!每次运行addHandler时,都会创建一个新的函数对象。
总结:
每次函数运行时,它都会创建一个新对象,其中包含el
的闭包。看到匿名函数维护对el
的访问,垃圾收集器无法将其从内存中删除。
anon函数将保持对el的访问,并且el可以访问该函数,即循环引用,这会导致IE中的内存泄漏。
答案 1 :(得分:8)
每当您在JavaScript中定义函数时,都会为其创建execution context;此执行上下文包含对作用域链中所有变量的引用,从全局作用域一直到本地作用域:
function test()
{
var el = document.getElementById('el');
el.onclick = function() {
// execution context of this function: el, test
alert('hello world');
}
}
当test()
完成时,匿名函数还没有被回收,因为它现在被分配给DOM的一个元素;即它是由DOM元素的属性引用。
同时,DOM元素本身也是函数执行上下文的一部分,现在由于循环引用而无法再循环,即使实际使用它并不是很明显;你可以在this answer找到演示。
那就是说,现在,大多数JavaScript引擎(甚至是那些在IE中找到的引擎)使用更高级的garbage collector,可以使用诸如mark-and-sweep或世代/短暂的技术更好地识别未使用的变量垃圾收集。
为了确保您不会在任何浏览器上遇到问题(但是,由于页面的典型生命周期,这主要是理论上的):
document.getElementById('el').onclick = function() {
alert('hello world');
}
答案 2 :(得分:2)
另请参阅有关此问题的MS文章的more information部分:
由于DOM对象是非JScript对象,因此发生此内存泄漏。 DOM对象不在标记和清除垃圾收集方案中 JScript中。因此,DOM对象和。之间的循环引用 在浏览器完全打开之前,JScript处理程序不会被破坏 撕下页面。
但请注意,与该文章中所述相反(当浏览器转到新页面时将回收内存),this article确认IE 6中的错误导致内存永久泄露。
答案 3 :(得分:1)
JavaScript的内存管理通常是这样的:“只要有可能达到它,就保持它”。这基本上是任何垃圾收集驱动的内存模型背后的范例。
垃圾收集器往往非常擅长他们的工作,他们甚至会检测某组元素是否只能在这组元素中到达。这些组也称为循环引用,因为如果您按照引用进行操作,则最终会访问您已访问过的元素:您已经运行了一个圆圈。
但是,在您的示例中,您实际上有两个来自两个不同“世界”的对象:
Internet Explorer使用自己的垃圾收集方案,与JavaScript使用的机制分开。两者之间的相互作用可能导致内存泄漏。
这正是发生的事情,可能导致内存泄漏。