Jeśli element DOM zostanie usunięty, czy jego detektory również zostaną usunięte z pamięci?

Odpowiedzi:

307

Nowoczesne przeglądarki

Zwykły JavaScript

Jeśli usunięty element DOM jest pozbawiony referencji (brak referencji do niego wskazujących), to tak - sam element jest odbierany przez moduł wyrzucający elementy bezużyteczne, a także powiązane z nim procedury obsługi / nasłuchiwania zdarzeń.

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.

Jednak; jeśli istnieją odniesienia, które nadal wskazują na ten element, element i detektory zdarzeń są zachowywane w pamięci.

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

Można byłoby założyć, że odpowiednie metody w jQuery (takie jak remove()) działałyby dokładnie w ten sam sposób (biorąc pod uwagę, że remove()zostały napisane przy użyciu removeChild()np.).

To jednak nie jest prawda ; biblioteka jQuery faktycznie ma wewnętrzną metodę (która jest nieudokumentowana i teoretycznie może być zmieniona w dowolnym momencie) o nazwie cleanData() (oto jak ta metoda wygląda ), która automatycznie usuwa wszystkie dane / zdarzenia związane z elementem po usunięciu z DOM (czy to przez. remove(), empty(), html("")itp).


Starsze przeglądarki

W starszych przeglądarkach - szczególnie w starszych wersjach IE - występują problemy z wyciekiem pamięci, ponieważ detektory zdarzeń przechowują odniesienia do elementów, do których zostały dołączone.

Jeśli chcesz bardziej dogłębnego wyjaśnienia przyczyn, wzorców i rozwiązań zastosowanych w celu usunięcia wycieków pamięci starszej wersji IE, w pełni zalecam przeczytanie tego artykułu MSDN na temat zrozumienia i rozwiązywania wzorców wycieków w programie Internet Explorer.

Kilka innych artykułów na ten temat:

Ręczne usuwanie słuchaczy prawdopodobnie byłoby dobrym nawykiem, aby się w tym przypadku przyzwyczaić (tylko jeśli pamięć jest tak ważna dla twojej aplikacji i faktycznie atakujesz takie przeglądarki).

dsgriffin
źródło
22
Zgodnie z Dokumentacją jquery przy użyciu metody remove () nad elementem, wszystkie detektory zdarzeń są usuwane z pamięci. Wpływa to na element, który sam siebie i na wszystkie węzły potomne. Jeśli chcesz zachować listę zdarzeń w pamięci, powinieneś użyć .detach (). Przydatne, gdy usunięte elementy będą ponownie wstawiane w domenie.
Lothre1
1
Jeśli element zawiera elementy potomne, czy odłączy także listy zdarzeń od elementów potomnych?
CBeTJlu4ok
1
@ Lothre1 - to tylko przy użyciu tej removemetody. przez większość czasu DOM jest po prostu całkowicie usuwany. (jak linki turbo lub coś takiego). Zastanawiam się, jak wpłynie to na pamięć, jeśli to zrobię document.body.innerHTML = ''...
vsync
1
Potrzebowałbym czegoś więcej niż „osobistego doświadczenia”, bardziej twardych danych i testów oraz linków do specyfikacji, które mówią, jak pamięć utrzymuje się w węzłach, których już nie ma w dokumencie, jest to zbyt ważne, aby zaufać komuś słowu bez dowodu :)
vsync
1
@ Lothre1 Dzięki - wykopałem trochę głębiej i dowiedziałem się, że pod tym względem jQuery działa inaczej niż zwykły JavaScript. Zaktualizowałem odpowiedź.
dsgriffin
23

w odniesieniu do jQuery:

metoda .remove () usuwa elementy z DOM. Użyj .remove (), jeśli chcesz usunąć sam element, a także wszystko, co się w nim znajduje. Oprócz samych elementów usuwane są wszystkie powiązane zdarzenia i dane jQuery powiązane z elementami. Aby usunąć elementy bez usuwania danych i zdarzeń, użyj zamiast tego metody .detach ().

Odniesienie: http://api.jquery.com/remove/

.remove()Kod źródłowy 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;
}

najwyraźniej używa jQuery node.removeChild()

Zgodnie z tym: 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.

tzn. detektory zdarzeń mogą zostać usunięte, ale nodenadal istnieją w pamięci.

Sreenath S.
źródło
3
Dodajesz tylko zamieszania - jQuery nie robi nic, removeChildco nie byłoby tak proste w przypadku programów obsługi . Oba również zwracają ci odniesienie, które możesz zachować, aby ponownie dołączyć (w takim przypadku oczywiście pozostaje w pamięci) lub rzucić drogę (w którym to przypadku jest on ostatecznie odbierany przez GC i usuwany).
Oleg V. Volkov
Wiem: D. więc gdzie jesteś tym, który zredagował pytanie? bo mogłem przysiąc, że w pytaniu było coś o użyciu jquery do usunięcia elementu DOM. teraz moja odpowiedź brzmi, jakbym wyjaśniał rzeczy, by pogłaskać moje ego. hej, zawsze możesz przegłosować
Sreenath S
8

Nie wahaj się obserwować sterty, aby zobaczyć wycieki pamięci w procedurach obsługi zdarzeń przechowujących odwołanie do elementu z zamknięciem oraz elementu przechowującego odwołanie do procedury obsługi zdarzeń.

Śmieciarka nie lubi okrągłych odniesień.

Przypadek przecieku pamięci: przyznaj, że obiekt ma odwołanie do elementu. Ten element ma odniesienie do modułu obsługi. I handler ma odwołanie do obiektu. Obiekt odnosi się do wielu innych obiektów. Ten obiekt był częścią kolekcji, którą Twoim zdaniem wyrzuciłeś, odwołując ją do swojej kolekcji. => cały obiekt i wszystko, do czego się odnosi, pozostanie w pamięci aż do wyjścia ze strony. => musisz pomyśleć o kompletnej metodzie zabijania dla swojej klasy obiektów lub zaufać np. ramce mvc.

Ponadto nie wahaj się skorzystać z części Drzewo zatrzymujące narzędzi programistycznych Chrome.

lib3d
źródło
8

Po prostu rozszerzam inne odpowiedzi ...

Programy obsługi zdarzeń delegowanych nie zostaną usunięte po usunięciu elementu.

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

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

Teraz sprawdź:

$._data(document.body, 'events');
Lucky Soni
źródło
9
Moduł obsługi zdarzeń jest dołączony do body, a nie #someEl, oczywiście nie należy go usuwać, dopóki body jest jeszcze w tym miejscu.
Yangshun Tay
Można to usunąć ręcznie: stackoverflow.com/questions/22400907/...
fangxing
7

W związku z jQuerytym następujące typowe metody usuwają również inne konstrukcje, takie jak procedury obsługi danych i zdarzeń:

usunąć()

Oprócz samych elementów usuwane są wszystkie powiązane zdarzenia i dane jQuery powiązane z elementami.

pusty()

Aby uniknąć wycieków pamięci, jQuery usuwa inne konstrukcje, takie jak procedury obsługi danych i zdarzeń z elementów potomnych przed usunięciem samych elementów.

html ()

Ponadto jQuery usuwa inne konstrukcje, takie jak procedury obsługi danych i zdarzeń z elementów potomnych przed zamianą tych elementów na nową treść.

Jaskey
źródło
2

Tak, śmieciarz również je usunie. Jednak nie zawsze tak jest w przypadku starszych przeglądarek.

Darin Dimitrov
źródło
7
instrukcja powinna być poparta dokumentacją API, przykładami itp.
Paolo Fulgoni