Якщо елемент 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.

Ще кілька статей, що стосуються цього:

Вручну вилучати слухачів вручну, мабуть, буде хорошою звичкою вступати в цьому випадку (лише якщо пам'ять є життєво важливою для вашої програми та ви фактично орієнтуєтесь на такі браузери).


22
Відповідно до Документації jquery, коли використовується метод delete () над елементом, усі слухачі подій видаляються з пам'яті. Це впливає на елемент, який він продає, і всі дочірні вузли. Якщо ви хочете зберегти списки подій у пам'яті, скористайтеся .detach (). Корисно, коли вилучені елементи будуть вставлені знову на купол.
Lothre1

1
Якщо елемент містить дочірні елементи, чи буде він також відлучати списки подій на дочірні елементи?
CBeTJlu4ok

1
@ Lothre1 - це лише при використанні removeметоду. більшу частину часу DOM просто стирається повністю. (наприклад, турбопосилання або щось таке). Мені цікаво, як впливає на пам'ять, якщо я роблю document.body.innerHTML = ''...
vsync

1
Мені знадобиться більше, ніж "особистий досвід", більше схожий на жорсткі дані та тести та посилання на характеристики, які говорять про те, як пам'ять зберігається на вузлах, яких більше немає в документі, це занадто важливо, щоб просто довірити чиємусь слову без доказів :)
vsync

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 та видаляється).
Олег Васильович Волков

я знаю: D. так де ти той, хто редагував питання? Тому що я міг би присягнути, що щось було у використанні jquery для видалення елемента DOM, у цьому питанні. тепер моя відповідь звучить так, що я пояснюю речі просто для того, щоб погладити своє его. ей, ти завжди можеш знищити
Sreenath S

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
Обробник події прикріплений до тіла, а не #someEl, природно, обробник не слід видаляти, доки тіло все ще знаходиться.
Yangshun Tay

Це можуть бути видалені вручну: stackoverflow.com/questions/22400907 / ...
Fangxing

7

Щодо того jQuery, наступні поширені методи також видалять інші конструкції, такі як обробники даних та подій:

видалити ()

Крім самих елементів, всі пов'язані події та дані jQuery, пов'язані з елементами, видаляються.

порожній ()

Щоб уникнути витоку пам'яті, jQuery видаляє інші конструкції, такі як обробники даних та події з дочірніх елементів, перш ніж видаляти самі елементи.

html ()

Крім того, jQuery видаляє інші конструкції, такі як обробники даних та події з дочірніх елементів, перш ніж замінити ці елементи новим вмістом.


2

Так, сміттєзбирач також видалить їх. Не завжди може бути справа зі застарілими браузерами.


7
Ваша заява повинна підтримуватися документацією API, прикладами тощо
Паоло Фулгоні,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.