如果删除了DOM元素,它的侦听器也会从内存中删除吗?

nim*_*rod 317 javascript memory jquery dom memory-leaks

如果删除了DOM元素,它的侦听器也会从内存中删除吗?

dsg*_*fin 286

现代浏览器

简单的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.
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

jQuery的

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

然而,事实并非如此 ; jQuery库实际上有一个内部方法(在任何时候都是未记录的,理论上可以随时更改)调用cleanData() (这是这个方法的样子),它从DOM中删除时自动清除与元素相关的所有数据/事件(通过是这样.remove(),empty(),html("")等等).


较旧的浏览器

旧的浏览器 - 特别是IE的旧版本 - 已知会因为事件侦听器保持对附加元素的引用而导致内存泄漏问题.

如果您想要更深入地解释用于修复旧版IE版本内存泄漏的原因,模式和解决方案,我完全建议您阅读这篇关于理解和解决Internet Explorer泄漏模式的MSDN文章.

还有一些与此相关的文章:

在这种情况下,手动删除监听器可能是一个很好的习惯(只有当内存对您的应用程序至关重要并且您实际上是针对此类浏览器时).

  • 根据jquery文档在元素上使用remove()方法时,所有事件侦听器都将从内存中删除.这会影响它所选择的元素和所有子节点.如果要将事件列表器保留在内存中,则应使用.detach().当删除的元素将再次插入dom时很有用. (21认同)
  • @Lothre1 - 仅在使用 `remove` 方法时。大多数情况下,DOM 会被完全擦除。(如涡轮链接或其他东西)。我想知道如果我执行 `document.body.innerHTML = ''` 会如何影响内存...... (2认同)
  • 我需要的不仅仅是“个人经验”,更像是硬数据和测试以及规范的链接,这些规范说明内存如何在不再存在于文档中的节点上保持持久性,这太重要了,不能在没有证据的情况下相信某人的话:) (2认同)
  • @Lothre1 谢谢 - 我已经深入挖掘并发现 jQuery 在这方面与常规 JavaScript 的行为有何不同。已更新答案。 (2认同)

Sre*_*h S 22

关于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;
}
Run Code Online (Sandbox Code Playgroud)

显然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仍然存在于内存中.

  • 你只是添加了混乱 - jQuery对处理程序没有做什么,简单的`removeChild`不会.两者都会返回一个引用,你可以继续重新附加后者(在这种情况下它显然保留在内存中)或抛出方式(在这种情况下它最终由GC拾取并删除). (3认同)

Luc*_*oni 8

只是扩展其他答案......

删除元素后,不会删除委派事件处理程序.

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

$('#someEL').remove(); // removing the element from DOM
Run Code Online (Sandbox Code Playgroud)

现在检查:

$._data(document.body, 'events');
Run Code Online (Sandbox Code Playgroud)

  • 事件处理程序附加到body而不是#someEl,当然,只要body仍然在这里,就不应该删除处理程序. (7认同)

lib*_*b3d 7

不要犹豫,看看堆在事件处理程序中看到内存泄漏,保持对带有闭包的元素的引用,并且元素保持对事件处理程序的引用.

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

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

此外,请不要犹豫使用Chrome开发工具的Retaining tree部分.


Jas*_*key 7

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

去掉()

除了元素本身之外,还删除了与元素关联的所有绑定事件和jQuery数据.

空()

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

HTML()

此外,在使用新内容替换这些元素之前,jQuery会从子元素中删除其他构造(如数据和事件处理程序).