Если элемент DOM удален, его слушатели также удаляются из памяти?

Если элемент DOM удален, его слушатели также удалены из памяти?

Ответ 1

Современные браузеры

Обычный 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.

Несколько статей, имеющих отношение к этому:

В ручном удалении самих слушателей, вероятно, будет хорошая привычка вмешиваться в этот случай (только если память важна для вашего приложения, и вы на самом деле нацеливаете на такие браузеры).

Ответ 2

относительно 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

Да

enter image description here

Обратите внимание, что я должен дважды нажать значок "force GC" из-за ошибки в Chrome DevTools. Из-за этого мой предыдущий gif был введен в заблуждение.

просачиванию memory.html:

<script>
function run() {

    function clicked(e) {
        console.log(e);
    }

    for (var i = 0; i < 1000; i++) {
        var span = document.createElement('span');
        span.addEventListener('click', clicked, false);
        document.body.appendChild(span);
    }

    setTimeout(function() {
        var spans = document.querySelectorAll('span');
        for (var i = 0; i < spans.length; i++) {
            var span = spans[i];
            span.parentNode.removeChild(span);
        }
    }, 100);

}
</script>
<button onclick="run()">Run (memory leak)</button>

нет-памяти leak.html

<script>
function run() {

    function clicked(e) {
        console.log(e);
    }

    for (var i = 0; i < 1000; i++) {
        var span = document.createElement('span');
        span.addEventListener('click', clicked, false);
        document.body.appendChild(span);
    }

    setTimeout(function() {
        var spans = document.querySelectorAll('span');
        for (var i = 0; i < spans.length; i++) {
            var span = spans[i];
            span.removeEventListener('click', clicked, false);
            span.remove();
        }
    }, 100);

}
</script>
<button onclick="run()">Run (no memory leak)</button>

Ответ 4

Не стесняйтесь смотреть кучу, чтобы увидеть утечки памяти в обработчиках событий, сохраняя ссылку на элемент с закрытием, и элемент, содержащий ссылку на обработчик событий.

Сборщик мусора не любит круговые ссылки.

Обычный случай утечки памяти: признать, что объект имеет ссылку на элемент. Этот элемент имеет ссылку на обработчик. И у обработчика есть ссылка на объект. Объект имеет ссылки на множество других объектов. Этот объект был частью коллекции, которую, по вашему мнению, вы выбрали, не выписывая ее из своей коллекции. = > весь объект и все, что он ссылается, останется в памяти до выхода страницы. = > вам следует подумать о полном методе убийства для вашего класса объектов или, например, доверять структуре mvc.

Кроме того, не стесняйтесь использовать часть "Сохранять дерево" в инструментах Chrome dev.

Ответ 5

Простое распространение других ответов...

Обработчики делегированных событий не будут удалены при удалении элемента.

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

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

Теперь проверьте:

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

Ответ 6

Что касается jQuery, следующие общие методы также удаляют другие конструкции, такие как данные и обработчики событий:

удалить()

В дополнение к самим элементам все связанные события и данные jQuery, связанные с элементами, удаляются.

empty()

Чтобы избежать утечек памяти, jQuery удаляет из дочерних элементов другие конструкторы, такие как данные и обработчики событий, перед удалением самих элементов.

html()

Кроме того, jQuery удаляет другие конструкторы, такие как обработчики данных и событий, из дочерних элементов, прежде чем заменять эти элементы новым контентом.

Ответ 7

Да, сборщик мусора также удалит их. Однако не всегда возможно использование старых браузеров.