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

Ответы:

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.

Еще несколько статей, относящихся к этому:

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

dsgriffin
источник
22
Согласно документации jquery при использовании метода remove () над элементом, все прослушиватели событий удаляются из памяти. Это влияет на элемент selft и все дочерние узлы. Если вы хотите сохранить списки событий в памяти, используйте вместо этого .detach (). Полезно, когда удаленные элементы будут вставлены снова в DOM.
Lothre1
1
Если элемент содержит дочерние элементы, будет ли он также отделять списки событий от дочерних элементов?
CBeTJlu4ok
1
@ Lothre1 - это только при использовании removeметода. Большую часть времени DOM будет просто полностью стёрт. (как турбо ссылки или что-то). Мне интересно, как влияет память, если я делаю document.body.innerHTML = ''...
vsync
1
Мне нужно больше, чем «личный опыт», больше похоже на точные данные и тесты, а также ссылки на спецификации, которые говорят о том, как память сохраняется на узлах, которых больше нет в документе, это слишком важно, чтобы просто доверять чьему-то слову без доказательств :)
vsync
1
@ Lothre1 Спасибо - я вырыл немного глубже и узнал, как jQuery действует в этом отношении иначе, чем обычный JavaScript. Обновили ответ.
Дсгриффин
23

относительно jQuery:

метод .remove () извлекает элементы из DOM. Используйте .remove (), когда вы хотите удалить сам элемент, а также все внутри него. В дополнение к самим элементам удаляются все связанные события и данные jQuery, связанные с элементами. Чтобы удалить элементы без удаления данных и событий, используйте вместо этого .detach ().

Ссылка: http://api.jquery.com/remove/

.remove()Исходный код jQuery v1.8.2 :

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все еще существуют в памяти.

Sreenath S
источник
3
Вы только добавляете путаницу - jQuery ничего не делает с обработчиками, которые так просто removeChildне делают . Оба также возвращают вам ссылку, которую вы можете сохранить, чтобы присоединить последнюю (в этом случае она, очевидно, остается в памяти) или выбрасывать (в этом случае GC в конечном итоге берется и удаляется).
Олег В. Волков
я знаю: D. так где ты тот кто редактировал вопрос? потому что я мог поклясться, что было что-то об использовании jquery для удаления элемента DOM, как было в предыдущем вопросе. теперь мой ответ звучит так, будто я объясняю вещи, чтобы погладить свое эго. эй, ты всегда можешь понизить голос
Sreenath S
8

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

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

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

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

lib3d
источник
8

Просто расширяю другие ответы ...

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

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

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

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

$._data(document.body, 'events');
Лаки Сони
источник
9
Обработчик события присоединяется к телу, а не к # someEl, естественно, обработчик не должен быть удален, пока тело все еще здесь.
Яншун Тэй
Это можно удалить вручную: stackoverflow.com/questions/22400907/…
fangxing
7

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

удалять()

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

пустой ()

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

HTML ()

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

Jaskey
источник
2

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

Дарин димитров
источник
7
Ваше