如果删除了DOM元素,是否还将其侦听器也从内存中删除了?


Answers:


307

现代浏览器

纯JavaScript

如果删除的DOM元素是无引用的(没有指向它的引用),则 -垃圾收集器以及与之关联的任何事件处理程序/侦听器都将拾取该元素本身。

var a = document.createElement('div');
var b = document.createElement('p');
// Add event listeners to b etc...
a.appendChild(b);
a.removeChild(b);
b = null; 
// A reference to 'b' no longer exists 
// Therefore the element and any event listeners attached to it are removed.

然而; 如果存在仍然指向所述元素的引用,则该元素及其事件侦听器将保留在内存中。

var a = document.createElement('div');
var b = document.createElement('p'); 
// Add event listeners to b etc...
a.appendChild(b);
a.removeChild(b); 
// A reference to 'b' still exists 
// Therefore the element and any associated event listeners are still retained.

jQuery的

假设jQuery中的相关方法(例如remove())将以完全相同的方式起作用(考虑remove()是使用removeChild()示例编写的),这是公平的。

但是,这不是真的;jQuery库实际上有一个内部方法(该方法尚未公开,理论上可以随时更改)cleanData() ,该方法称为(此方法看起来像这样),该方法会在从DOM中删除后自动清除与元素关联的所有数据/事件。 (通过是这样。remove()empty()html("")等等)。


较旧的浏览器

已知较旧的浏览器(尤其是IE的较旧版本)会出现内存泄漏问题,这是因为事件侦听器保留了对其所附加元素的引用。

如果您想更深入地解释用于解决旧版IE版本内存泄漏的原因,模式和解决方案,我完全建议您阅读此MSDN文章,了解和解决Internet Explorer泄漏模式。

一些与此相关的文章:

在这种情况下,您自己可以手动删除监听器,这可能是一个好习惯(仅当内存对于您的应用程序至关重要,而您实际上是针对此类浏览器的时候)。


22
根据jquery文档,在元素上使用remove()方法时,所有事件侦听器都将从内存中删除。这会影响到它自身的元素和所有子节点。如果要将事件列表保存在内存中,则应改用.detach()。当删除的元素将再次插入dom时很有用。
Lothre1

1
如果元素包含子元素,是否也会分离子元素上的事件列表?
CBeTJlu4ok 2014年

1
@ Lothre1-仅在使用该remove方法时。大多数情况下,DOM只会被完全擦除。(例如涡轮链接等)。我想知道如果我这样做document.body.innerHTML = ''
会对

1
我将需要的不仅仅是“个人经验”,更像是硬数据和测试以及指向规范的链接,这些规范说内存如何在文档中不再存在的节点上保持持久性,这太重要了,以至于只能凭空证明某人的话:)
vsync 2014年

1
@ Lothre1谢谢-我已经更深入地研究了,发现jQuery在这方面与常规JavaScript有何不同。更新了答案。
dsgriffin

23

关于jQuery:

.remove()方法将元素移出DOM。要删除元素本身以及其中的所有内容时,请使用.remove()。除了元素本身之外,所有绑定的事件和与元素相关联的jQuery数据都将被删除。要删除元素而不删除数据和事件,请改用.detach()。

参考:http : //api.jquery.com/remove/

jQuery v1.8.2 .remove()源代码:

remove: function( selector, keepData ) {
    var elem,
        i = 0;

    for ( ; (elem = this[i]) != null; i++ ) {
        if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
            if ( !keepData && elem.nodeType === 1 ) {
                jQuery.cleanData( elem.getElementsByTagName("*") );
                jQuery.cleanData( [ elem ] );
            }

            if ( elem.parentNode ) {
                elem.parentNode.removeChild( elem );
            }
        }
    }

    return this;
}

显然jQuery使用 node.removeChild()

据此:https : //developer.mozilla.org/en-US/docs/DOM/Node.removeChild

The removed child node still exists in memory, but is no longer part of the DOM. You may reuse the removed node later in your code, via the oldChild object reference.

即事件监听器可能会被删除,但node仍存在于内存中。


3
您只会增加混乱-jQuery对于简单的处理程序不会做任何事情removeChild。两者都还为您提供了一个参考,您可以保留该参考以重新附加后者(在这种情况下,它显然保留在内存中)或抛出方式(在这种情况下,它最终会被GC拾取并删除)。
Oleg V. Volkov 2012年

我知道:D。那么,您编辑问题的人在哪里?COS我可能发誓,在前面的问题中,有一些关于使用jquery删除DOM元素的事情。现在,我的回答听起来像是我在解释事情,只是在抚摸我的自我。嘿,您可以随时
投票

8

不要犹豫地观看堆,以查看事件处理程序中的内存泄漏,该事件处理程序使用闭包保留对该元素的引用,而该元素保留对事件处理程序的引用。

垃圾收集器不喜欢循环引用。

通常的内存泄漏情况:承认对象具有对元素的引用。该元素具有对处理程序的引用。处理程序具有对该对象的引用。该对象引用了许多其他对象。该对象是您认为已从集合中取消引用它而丢弃的集合的一部分。=>整个对象及其引用的所有对象将保留在内存中,直到页面退出。=>您必须为对象类考虑一种完整的杀死方法,或者例如信任mvc框架。

此外,请不要犹豫使用Chrome开发人员工具的“保留树”部分。


8

只是扩展其他答案...

委派的事件处理程序将在元素删除后也不会被删除。

$('body').on('click', '#someEl', function (event){
  console.log(event);
});

$('#someEL').remove(); // removing the element from DOM

现在检查:

$._data(document.body, 'events');

9
事件处理程序是附加在body上的,而不是附加在#someEl上的,当然,只要body仍在此处,则不应删除该处理程序。
Yangshun Tay


7

关于jQuery,以下常用方法还将删除其他构造,例如数据和事件处理程序:

去掉()

除了元素本身之外,所有绑定的事件和与元素相关联的jQuery数据都将被删除。

空()

为了避免内存泄漏,jQuery在删除元素本身之前先从子元素中删除其他构造,例如数据和事件处理程序。

html()

此外,在用新内容替换那些元素之前,jQuery从子元素中删除了其他构造,例如数据和事件处理程序。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.