DOM节点上的事件处理程序是否会被节点删除?

Nat*_*ong 32 javascript dom javascript-events

(注意:我在下面使用jQuery,但问题实际上是一般的Javascript问题.)

假设我有一个div#formsection使用AJAX反复更新其内容的内容,如下所示:

var formSection = $('div#formsection');
var newContents = $.get(/* URL for next section */);
formSection.html(newContents);
Run Code Online (Sandbox Code Playgroud)

每当我更新这个div时,我会触发一个自定义事件,它将事件处理程序绑定到一些新添加的元素,如下所示:

// When the first section of the form is loaded, this runs...
formSection.find('select#phonenumber').change(function(){/* stuff */});

...

// ... when the second section of the form is loaded, this runs...
formSection.find('input#foo').focus(function(){/* stuff */});
Run Code Online (Sandbox Code Playgroud)

所以:我将事件处理程序绑定到某些DOM节点,然后删除这些DOM节点并插入新节点(html()这样做)并将事件处理程序绑定到新的DOM节点.

我的事件处理程序是否与它们绑定的DOM节点一起被删除?换句话说,当我加载新的部分时,很多无用的事件处理程序堆积在浏览器内存中,等待不再存在的DOM节点上的事件,或者当它们的DOM节点被删除时它们被清除了吗?

奖金问题:如何自己测试?

Moo*_*Goo 22

事件处理函数受其他变量的垃圾收集.这意味着当解释器确定没有可能获得对函数的引用的方法时,它们将从内存中删除.但是,简单地删除节点并不能保证垃圾收集.例如,获取此节点和关联的事件处理程序

var node = document.getElementById('test');
node.onclick = function() { alert('hai') };
Run Code Online (Sandbox Code Playgroud)

现在让我们从DOM中删除节点

node.parentNode.removeChild(node);
Run Code Online (Sandbox Code Playgroud)

所以node将不再在您的网站上可见,但它显然仍然存在于内存中,事件处理程序也是如此

node.onclick(); //alerts hai
Run Code Online (Sandbox Code Playgroud)

只要node仍可以某种方式访问引用,它的相关属性(其中onclick一个)将保持不变.

现在让我们尝试一下,不要创建一个悬空变量

document.getElementById('test').onclick = function() { alert('hai'); }

document.getElementById('test').parentNode.removeChild(document.getElementById('test'));
Run Code Online (Sandbox Code Playgroud)

在这种情况下,似乎没有其他方法可以访问DOM节点#test,因此当运行垃圾收集周期时,onclick应该从内存中删除处理程序.

但这是一个非常简单的案例.Javascript使用闭包可能会极大地复杂化垃圾收集性的确定.让我们尝试将稍微复杂的事件处理函数绑定到onclick

document.getElementById('test').onclick = function() {
  var i = 0;
  setInterval(function() {
    console.log(i++);
  }, 1000);

  this.parentNode.removeChild(this);
};
Run Code Online (Sandbox Code Playgroud)

因此,当您单击#test时,元素将立即被删除,但是一秒钟之后,之后每秒,您将看到一个增加的数字打印到您的控制台.该节点被删除,并且不可能进一步引用它,但似乎它的一部分仍然存在.在这种情况下,事件处理函数本身可能不会保留在内存中,但它创建的范围是.

所以答案我猜是; 这取决于.如果存在对已删除DOM节点的悬空可访问引用,则它们的关联事件处理程序仍将与其余属性一起驻留在内存中.即使不是这种情况,事件处理程序函数创建的作用域可能仍在使用中并且在内存中.

在大多数情况下(并且高兴地忽略IE6)最好只相信垃圾收集器来完成它的工作,毕竟Javascript不是C. 但是,在最后一个示例的情况下,编写某种类型的析构函数来隐式关闭功能非常重要.


Jam*_*acs 6

当从DOM中删除元素时,jQuery竭尽全力避免内存泄漏.只要您使用jQuery删除DOM节点,就应该通过jQuery处理事件处理程序和额外数据的删除.我强烈建议阅读John Resig的JavaScript忍者秘密,因为他详细介绍了不同浏览器中的潜在泄漏以及jQuery等JavaScript库如何解决这些问题.如果您不使用jQuery,则在删除DOM节点时,您必须担心通过孤立事件处理程序泄漏内存.