Jika Elemen DOM dihapus, apakah pendengarnya juga dihapus dari memori?

Jawaban:

307

Browser modern

JavaScript polos

Jika elemen DOM yang dihapus bebas referensi (tidak ada referensi yang menunjukkannya), maka ya - elemen itu sendiri diambil oleh pengumpul sampah serta penangan / pendengar acara yang terkait dengannya.

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.

Namun; jika ada referensi yang masih menunjuk ke elemen tersebut, elemen dan pendengar acara tetap disimpan dalam memori.

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

Akan adil untuk mengasumsikan bahwa metode yang relevan di jQuery (seperti remove()) akan berfungsi dengan cara yang persis sama (mengingat remove()ditulis menggunakan menggunakan removeChild()misalnya).

Namun, ini tidak benar ; pustaka jQuery sebenarnya memiliki metode internal (yang tidak berdokumen dan secara teori dapat diubah kapan saja) yang disebut cleanData() (inilah yang terlihat seperti metode ini ) yang secara otomatis membersihkan semua data / peristiwa yang terkait dengan elemen setelah dihapus dari DOM (menjadi ini melalui. remove(), empty(), html("")dll).


Browser yang lebih lama

Peramban yang lebih lama - khususnya versi IE yang lebih lama - diketahui memiliki masalah kebocoran memori karena pendengar acara tetap memegang referensi ke elemen yang dilampirkan.

Jika Anda menginginkan penjelasan yang lebih mendalam tentang penyebab, pola, dan solusi yang digunakan untuk memperbaiki kebocoran memori versi IE lama, saya sepenuhnya menyarankan Anda membaca artikel MSDN ini tentang Memahami dan Memecahkan Pola Kebocoran Internet Explorer.

Beberapa artikel yang relevan dengan ini:

Menghapus sendiri pendengar secara manual mungkin merupakan kebiasaan yang baik untuk digunakan dalam kasus ini (hanya jika ingatannya begitu vital bagi aplikasi Anda dan Anda benar-benar menargetkan browser semacam itu).

dsgriffin
sumber
22
Menurut Dokumentasi jquery saat menggunakan metode remove () di atas elemen, semua pendengar acara dihapus dari memori. Ini memengaruhi elemen yang dipilihnya dan semua node anak. Jika Anda ingin menyimpan daftar acara dalam memori, Anda harus menggunakan .detach () sebagai gantinya. Berguna ketika elemen yang dihapus akan dimasukkan lagi pada dom.
Lothre1
1
Jika elemen tersebut mengandung child elemens, apakah elemen itu juga akan melepaskan listner event pada elemen child?
CBeTJlu4ok
1
@ Lothre1 - itu hanya saat menggunakan removemetode. sebagian besar waktu DOM hanya akan terhapus sepenuhnya. (seperti tautan turbo atau sesuatu). Saya bertanya-tanya bagaimana memori terpengaruh jika saya lakukan document.body.innerHTML = ''...
vsync
1
Saya akan membutuhkan lebih dari "pengalaman pribadi", lebih seperti data keras dan tes dan tautan ke spesifikasi yang mengatakan bagaimana memori tetap ada pada node yang tidak lagi ada dalam dokumen, ini terlalu penting untuk hanya mempercayai kata-kata seseorang tanpa bukti :)
vsync
1
@ Lothre1 Terima kasih - Saya telah menggali sedikit lebih dalam dan menemukan bagaimana jQuery bertindak berbeda dari JavaScript biasa dalam hal ini. Telah memperbarui jawabannya.
dsgriffin
23

tentang jQuery:

metode .remove () mengeluarkan elemen dari DOM. Gunakan .remove () saat Anda ingin menghapus elemen itu sendiri, serta semua yang ada di dalamnya. Selain elemen itu sendiri, semua acara terikat dan data jQuery yang terkait dengan elemen dihapus. Untuk menghapus elemen tanpa menghapus data dan acara, gunakan .detach () sebagai gantinya.

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

.remove()kode sumber 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;
}

rupanya jQuery menggunakan node.removeChild()

Menurut ini: 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.

yaitu pendengar acara mungkin bisa dihapus, tetapi nodemasih ada dalam memori.

Sreenath S
sumber
3
Anda hanya menambahkan kebingungan - jQuery tidak melakukan apa pun dengan penangan yang sesederhana removeChilditu. Keduanya juga mengembalikan Anda referensi yang Anda dapat tetap menempel kembali yang terakhir (dalam hal ini jelas tetap dalam memori) atau membuang cara (dalam hal ini akhirnya diambil oleh GC dan dihapus).
Oleg V. Volkov
aku tahu: D. jadi di mana Anda orang yang mengedit pertanyaan? karena saya bisa bersumpah bahwa ada sesuatu tentang menggunakan jquery untuk menghapus elemen DOM, dalam pertanyaan sebelumnya. sekarang jawaban saya terdengar seperti saya sedang menjelaskan hal-hal hanya untuk memukul ego saya. hei Anda selalu dapat downvote
Sreenath S
8

Jangan ragu untuk menonton tumpukan untuk melihat kebocoran memori di penangan acara menjaga referensi ke elemen dengan penutupan dan elemen menjaga referensi ke penangan acara.

Pengumpul sampah tidak suka referensi lingkaran.

Kasus kebocoran memori biasa: mengakui suatu objek memiliki referensi ke elemen. Elemen itu memiliki referensi ke pawang. Dan pawang memiliki referensi ke objek. Objek memiliki referensi ke banyak objek lain. Objek ini adalah bagian dari koleksi yang Anda pikir telah Anda buang dengan menghapus referensi dari koleksi Anda. => seluruh objek dan semua yang dirujuk akan tetap ada dalam memori sampai halaman keluar. => Anda harus berpikir tentang metode pembunuhan lengkap untuk kelas objek Anda atau mempercayai kerangka kerja mvc misalnya.

Selain itu, jangan ragu untuk menggunakan bagian Penahan pohon alat dev Chrome.

lib3d
sumber
8

Hanya memperluas jawaban lain ...

Penangan event yang didelegasikan tidak akan dihapus setelah penghapusan elemen.

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

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

Sekarang periksa:

$._data(document.body, 'events');
Soni yang beruntung
sumber
9
Event handler melekat pada body dan bukan #someEl, tentu saja handler tidak boleh dilepas asalkan body masih ada di sini.
Yangshun Tay
Ini dapat dihapus secara manual: stackoverflow.com/questions/22400907/…
fangxing
7

Mengenai jQuery, metode umum berikut juga akan menghapus konstruksi lainnya seperti data dan penangan peristiwa:

menghapus()

Selain elemen itu sendiri, semua acara terikat dan data jQuery yang terkait dengan elemen dihapus.

kosong()

Untuk menghindari kebocoran memori, jQuery menghapus konstruksi lain seperti data dan penangan peristiwa dari elemen anak sebelum menghapus elemen itu sendiri.

html ()

Selain itu, jQuery menghapus konstruksi lain seperti data dan penangan peristiwa dari elemen anak sebelum mengganti elemen-elemen itu dengan konten baru.

Jaskey
sumber
2

Ya, pemulung juga akan membuangnya. Mungkin tidak selalu demikian halnya dengan browser lawas.

Darin Dimitrov
sumber
7
Pernyataan Anda harus didukung oleh dokumentasi API, contoh, dll.
Paolo Fulgoni