Nếu một phần tử DOM bị xóa, trình nghe của nó cũng bị xóa khỏi bộ nhớ?


Câu trả lời:


307

Trình duyệt hiện đại

JavaScript đơn giản

Nếu một phần tử DOM bị loại bỏ không có tham chiếu (không có tham chiếu trỏ đến nó) thì - phần tử được chọn bởi trình thu gom rác cũng như bất kỳ trình xử lý / trình lắng nghe sự kiện nào liên quan đến nó.

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.

Tuy nhiên; nếu có các tham chiếu vẫn trỏ đến phần tử đã nói, phần tử và phần tử lắng nghe sự kiện của nó được giữ lại trong bộ nhớ.

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

Sẽ công bằng khi giả định rằng các phương thức có liên quan trong jQuery (chẳng hạn như remove()) sẽ hoạt động theo cùng một cách chính xác (xem xét remove()được viết bằng cách sử dụng removeChild()chẳng hạn).

Tuy nhiên, điều này không đúng ; thư viện jQuery thực sự có một phương thức bên trong (không có tài liệu và về lý thuyết có thể thay đổi bất cứ lúc nào) được gọi cleanData() (đây là phương thức này trông như thế nào ) tự động dọn sạch tất cả dữ liệu / sự kiện liên quan đến một phần tử khi xóa khỏi DOM (được điều này thông qua. remove(), empty(), html("")vv).


Các trình duyệt cũ hơn

Các trình duyệt cũ hơn - cụ thể là các phiên bản IE cũ hơn - được biết là có vấn đề rò rỉ bộ nhớ do người nghe sự kiện giữ các tham chiếu đến các yếu tố mà chúng được đính kèm.

Nếu bạn muốn giải thích sâu hơn về nguyên nhân, mẫu và giải pháp được sử dụng để khắc phục rò rỉ bộ nhớ phiên bản IE cũ, tôi hoàn toàn khuyên bạn nên đọc bài viết MSDN này về Tìm hiểu và Giải quyết các mẫu rò rỉ Internet Explorer.

Một vài bài viết liên quan đến điều này:

Tự mình loại bỏ người nghe có lẽ sẽ là một thói quen tốt trong trường hợp này (chỉ khi bộ nhớ là quan trọng đối với ứng dụng của bạn và bạn thực sự đang nhắm mục tiêu các trình duyệt như vậy).


22
Theo Tài liệu jquery khi sử dụng phương thức remove () trên một phần tử, tất cả các trình lắng nghe sự kiện được xóa khỏi bộ nhớ. Điều này ảnh hưởng đến phần tử mà nó chọn và tất cả các nút con. Nếu bạn muốn giữ những người nghe sự kiện trong bộ nhớ, bạn nên sử dụng .detach (). Hữu ích khi các phần tử bị loại bỏ sẽ được chèn lại trên dom.
Lothre1

1
Nếu phần tử chứa elemens con, nó cũng sẽ tách người nghe sự kiện trên các phần tử con chứ?
CBeTJlu4ok

1
@ Lothre1 - đó chỉ là khi sử dụng removephương thức. hầu hết thời gian DOM sẽ bị xóa hoàn toàn. (như liên kết turbo hoặc một cái gì đó). Tôi tự hỏi bộ nhớ bị ảnh hưởng như thế nào nếu tôi làm document.body.innerHTML = ''...
vsync

1
Tôi sẽ cần nhiều hơn "trải nghiệm cá nhân", giống như dữ liệu cứng và các bài kiểm tra và liên kết đến thông số kỹ thuật cho biết cách bộ nhớ duy trì liên tục trên các nút không còn trong tài liệu, điều này quá quan trọng để chỉ tin vào lời nói của ai đó mà không cần bằng chứng :)
vsync

1
@ Lothre1 Cảm ơn - Tôi đã đào sâu hơn một chút và tìm hiểu cách jQuery hoạt động khác với JavaScript thông thường về mặt này. Đã cập nhật câu trả lời.
dsgriffin 10/2/2015

23

liên quan đến jQuery:

phương thức .remove () lấy các phần tử ra khỏi DOM. Sử dụng .remove () khi bạn muốn loại bỏ phần tử đó, cũng như mọi thứ bên trong nó. Ngoài các phần tử, tất cả các sự kiện bị ràng buộc và dữ liệu jQuery liên quan đến các phần tử được loại bỏ. Để xóa các phần tử mà không xóa dữ liệu và sự kiện, hãy sử dụng .detach () để thay thế.

Tham khảo: http://api.jquery.com/remove/

.remove()Mã nguồn 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;
}

rõ ràng jQuery sử dụng node.removeChild()

Theo đó: 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.

tức là người nghe sự kiện có thể bị xóa, nhưng nodevẫn tồn tại trong bộ nhớ.


3
Bạn chỉ thêm nhầm lẫn - jQuery không làm gì với trình xử lý đơn giản removeChildnhư vậy. Cả hai cũng trả về cho bạn một tham chiếu mà bạn có thể giữ lại để gắn lại sau này (trong trường hợp đó rõ ràng nó vẫn còn trong bộ nhớ) hoặc cách ném (trong trường hợp đó cuối cùng nó được chọn bởi GC và loại bỏ).
Oleg V. Volkov

tôi biết. Vì vậy, nơi bạn là người chỉnh sửa câu hỏi? Vì tôi có thể đã thề rằng có một cái gì đó về việc sử dụng jquery để loại bỏ một phần tử DOM, trong câu hỏi trước đây. bây giờ câu trả lời của tôi nghe có vẻ như tôi đang giải thích mọi thứ chỉ để vuốt ve cái tôi của mình. này, bạn luôn có thể downvote
Sreenath S

8

Đừng ngần ngại xem heap để xem rò rỉ bộ nhớ trong các trình xử lý sự kiện giữ tham chiếu đến phần tử với phần đóng và phần tử giữ tham chiếu đến trình xử lý sự kiện.

Người thu gom rác không thích tài liệu tham khảo vòng tròn.

Trường hợp rò rỉ bộ nhớ thông thường: thừa nhận một đối tượng có tham chiếu đến một phần tử. Yếu tố đó có một ref cho người xử lý. Và xử lý có một ref cho đối tượng. Đối tượng có ref cho rất nhiều đối tượng khác. Đối tượng này là một phần của bộ sưu tập mà bạn nghĩ rằng bạn đã vứt bỏ bằng cách hủy bỏ nó khỏi bộ sưu tập của bạn. => toàn bộ đối tượng và tất cả những gì nó đề cập sẽ vẫn còn trong bộ nhớ cho đến khi thoát khỏi trang. => bạn phải suy nghĩ về một phương thức tiêu diệt hoàn toàn cho lớp đối tượng của bạn hoặc tin tưởng vào khung mvc chẳng hạn.

Hơn nữa, đừng ngần ngại sử dụng phần Cây giữ lại của các công cụ phát triển Chrome.


8

Chỉ cần mở rộng câu trả lời khác ...

Trình xử lý sự kiện được ủy nhiệm sẽ không bị xóa khi xóa phần tử.

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

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

Kiểm tra bây giờ:

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

9
Trình xử lý sự kiện được gắn vào thân máy chứ không phải #someEl, tự nhiên trình xử lý không nên được gỡ bỏ miễn là cơ thể vẫn còn ở đây.
Yangshun Tay

Điều này có thể được gỡ bỏ bằng tay: stackoverflow.com/questions/22400907/ từ
fangxing

7

Về jQuerycác phương thức phổ biến sau đây cũng sẽ loại bỏ các cấu trúc khác như xử lý dữ liệu và sự kiện:

tẩy()

Ngoài các phần tử, tất cả các sự kiện bị ràng buộc và dữ liệu jQuery liên quan đến các phần tử được loại bỏ.

trống()

Để tránh rò rỉ bộ nhớ, jQuery loại bỏ các cấu trúc khác như xử lý dữ liệu và sự kiện khỏi các phần tử con trước khi tự loại bỏ các phần tử.

html ()

Ngoài ra, jQuery loại bỏ các cấu trúc khác như xử lý dữ liệu và sự kiện khỏi các phần tử con trước khi thay thế các phần tử đó bằng nội dung mới.


2

Có, người thu gom rác cũng sẽ loại bỏ chúng. Có thể không phải luôn luôn là trường hợp với các trình duyệt cũ.


7
tuyên bố của bạn nên được hỗ trợ bởi tài liệu API, ví dụ, v.v.
Paolo Fulgoni
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.