2012-12-14 32 views
22

Tôi chưa gặp sự cố rò rỉ bộ nhớ trong ứng dụng của mình, nhưng tôi lo lắng về các sự cố có thể xảy ra trong tương lai. Tôi muốn biết nếu bạn làm điều gì đó như thế này:Trình xử lý sự kiện, bao đóng và thu gom rác trong Javascript

SomeClass.prototype.someMethod= function() { 
    var that= this 
    this.$div2.click(function() { 
     that.someMethod2(); 
    }); 
} 

Và giả sử điều này. $ Div2 được thêm vào div khác. $ Div1. Nếu tôi gọi

this.$div1.remove(); 

và sau đó mất tham chiếu của cá thể SomeClass của tôi, ví dụ SomeClass có bị thu gom rác không? Và điều gì về phần tử HTML này. $ Div2? điều này. $ div2 sẽ không nằm trong DOM vì nó được nối vào nó. $ div1.

Tôi hỏi điều này vì trình xử lý sự kiện trong này. $ Div2 có thể giữ tham chiếu đến phần tử HTML này. $ Div2 và cũng giữ tham chiếu đến thể hiện của SomeClass thông qua việc đóng vì biến "đó".

Vì vậy, tôi nên quan tâm đến việc xóa đúng tất cả các sự kiện và các phần tử HTML như thế này? Hoặc đơn giản là xóa phần tử "root" (điều này. $ Div1) giải quyết được vấn đề?

+0

Tôi nghĩ điều đó sẽ xảy ra. Các nhà sưu tập rác nên theo dõi các tài liệu tham khảo, do đó, trong lý thuyết thời điểm tài liệu tham khảo cuối cùng biến mất, mục này phải là một ứng cử viên cho việc thu gom rác thải. Tôi tự hỏi nếu những người trong JS động cơ (s) cũng có thể phân tích mã unreachable và đánh dấu các mục vẫn còn có tài liệu tham khảo nhưng có tài liệu tham khảo là không sử dụng ... – RonaldBarzell

+0

Nó phụ thuộc. Với một webapp cổ điển, điều này không thực sự là một vấn đề, vì bạn thường xuyên gửi đến máy chủ, tải hoặc làm mới một chế độ xem khác. Với các ứng dụng trang đơn mới nổi, điều này có thể là một vấn đề, đặc biệt là với các trình duyệt 'đã chết' như IE. – asgoth

+0

@asgoth Tôi đang tạo một ứng dụng trang đơn. Một trong đó phải chạy đáng tin cậy trong ít nhất 12 giờ mà không làm mới trang, do đó lo lắng của tôi về việc thu gom rác thải. – Hoffmann

Trả lời

15

this.$div2 được nối vào this.$div1. Nếu tôi gọi this.$div1.remove(); và sau đó mất tài liệu tham khảo của cá thể SomeClass của tôi thì cá thể SomeClass có bị thu gom rác không?

Có, khi tất cả các tham chiếu đến nó bị mất - cũng như thông qua trình xử lý sự kiện, ví dụ có thể bị thu gom rác.

Còn phần tử HTML this.$div2 thì sao? this.$div2 sẽ không nằm trong DOM vì nó được thêm vào this.$div1.

Việc cho dù hiện tại có được đính kèm với DOM hay không. Nếu một số tham chiếu đối tượng không thu thập được $div1, nó cũng có thể truy cập nút con của nó $div2 và trình xử lý sự kiện của một người, do đó trường hợp được tham chiếu từ trình xử lý sẽ không thể thu thập được.

Tôi yêu cầu này bởi vì xử lý sự kiện trong this.$div2 có thể giữ một tham chiếu đến các phần tử HTML this.$div2 và cũng giữ một tham chiếu đến thể hiện của SomeClass thông qua việc đóng cửa vì biến "mà".

Đó là một tham chiếu vòng tròn và nên được xử lý tốt bằng các công cụ (khi không ai trong số các đối tượng bên trong vòng tròn được tham chiếu từ bên ngoài nó có thể được thu thập). Tuy nhiên, (cũ?) Internet Explorers không làm điều này khi một đối tượng DOM được tham gia vào vòng tròn.

Vì lý do đó, .remove jQuery method (code) gọi nội bộ số (internal) cleanData method làm gián đoạn tất cả người nghe sự kiện.

Vì vậy, tôi nên quan tâm đến việc xóa đúng tất cả các sự kiện và phần tử HTML như thế này? Hoặc đơn giản là xóa phần tử "root" (điều này. $ Div1) giải quyết được vấn đề?

Có, gọi remove trên trình bao bọc jQuery tự động xóa tất cả các sự kiện (từ tất cả các phần tử con) và nút DOM.

7

Tôi có nên quan tâm đến việc xóa đúng tất cả các sự kiện và thành phần HTML như thế này?

Câu trả lời ngắn gọn là Không! ít nhất trong 99% các trường hợp, nó sẽ không quan trọng trong bất kỳ cách nào vì bộ nhớ được sử dụng bởi một phần tử DOM là tầm thường so với bộ nhớ tổng thể được sử dụng bởi một trang web. Tuy nhiên nó luôn luôn là một thực hành tốt để phát hành bộ nhớ được sử dụng bởi xử lý các đối tượng không cần thiết, nhưng bạn không thể nói rằng GC chắc chắn sẽ phát hành bộ nhớ được sử dụng bởi các yếu tố bởi vì thu gom rác là hoàn toàn lên đến trình duyệt! Về lý thuyết GC chỉ nên khởi động khi không có tham chiếu đến phần tử DOM, ít nhất đó là cách Chrome works, nhưng bằng các ngôn ngữ như JavaScript, bạn không nói rõ thời gian chạy bạn đã hoàn thành với đối tượng, mọi thứ trở nên lộn xộn trong JavaScript một cách nhanh chóng: một hàm có thể truyền đối tượng vào một số hàm khác, đối tượng có thể được lưu lại như một thành viên trong một đối tượng khác, một đối tượng có thể được tham chiếu thông qua việc đóng vv, vì vậy nó hoàn toàn phụ thuộc vào trình duyệt như thế nào và những gì để thu thập!

Trong trường hợp bạn xóa div1 giải phóng tài liệu html và phần tử sẽ không hiển thị trong chế độ xem, trên thực tế, phương pháp remove của jQuery sẽ loại bỏ tất cả các sự kiện, thuộc tính expando và phần tử con được gắn với phần tử cùng với phần tử chính nó, tuy nhiên bạn giữ một tham chiếu của div1div2 trong một đối tượng khác làm cho cả hai phần tử DOM Các phần tử Orphan! loại bỏ SomeClass biến mẫu giải phóng tất cả các tham chiếu đến các phần tử DOM làm cho chúng trở thành ứng cử viên cho việc thu gom rác nhưng ở đây có biến số that phức tạp khiến nguyên tố DOM thực hiện tham chiếu đến cá thể SomeClass thông qua clusure! Vấn đề này được biết đến như Circular Reference trong IE:

Đối tượng JavaScript và các yếu tố DOM lưu trữ tham chiếu tới một nguyên nhân khác thu gom rác của Internet Explorer để không đòi lại bộ nhớ , dẫn đến rò rỉ bộ nhớ

enter image description here

You can read more about it here

Sự rò rỉ đặc biệt này chủ yếu là lịch sử quan tâm đến IE < 8, nhưng một ví dụ tốt về việc phá vỡ liên kết vòng tròn là tránh sử dụng biến số that, thay vào đó hãy sử dụng proxy hoặc delegate để thay đổi ngữ cảnh của trình xử lý sự kiện thành một số ngữ cảnh cụ thể.

ECMA 5th bind method là bỏ thuốc lá thay đổi bối cảnh hữu ích khi nói đến xử lý sự kiện DOM, đây là một bộ xử lý đơn giản dựa trên mã của bạn mà không sử dụng biến đóng cửa:

this.$div2.click((function() { 
     this.someMethod2(); 
    }).bind(this)); 
+0

"Mẹo" của bạn để tránh biến 'that' là vô dụng, vì giá trị vẫn còn bị ràng buộc với hàm đó. Btw, bạn sẽ sử dụng 'this. $ Div2.click (this.someMethod2.bind (this));', biểu thức hàm ẩn danh là không cần thiết. – Bergi

+1

Tôi tin rằng bạn không chính xác Bergi, trong ví dụ trên 'this' đang tham chiếu đến phần tử DOM, tuy nhiên không có cách nào phần tử DOM tham chiếu cá thể đối tượng biết rằng' bind' thực sự đang tạo một hàm ** hoàn toàn mới ** , khi được gọi, có từ khóa 'this' được đặt thành giá trị được cung cấp. Vì vậy, về cơ bản đó là một liên kết bị hỏng giữa hai, BTW đẹp bắt, chức năng vô danh là khá vô dụng –

+1

Tôi nghĩ rằng DOM-to-dụ tham chiếu đã thông qua chức năng gọi lại đó? Tuy nhiên, điều tôi muốn nói là sử dụng 'bind' hoàn toàn không khác với việc sử dụng biểu thức hàm' that' variable +. – Bergi

1

Nếu bạn sẽ tạo ra yếu tố động, sau đó gán cho họ các sự kiện . tôi nghĩ rằng mã của bạn không phải là một cách hay để làm điều đó. bạn nên làm theo cách này:

cho các yếu tố cố định nếu bạn cần một sự kiện, sử dụng hai chức năng này; cái đầu tiên được gọi trong hàm tạo, cái thứ hai trong hàm hủy.

on_Events: function() { 
    $('your_form').on('event_name', {element_Selector}, callback_function) 
}, 
off_Events: function() { 
    $('your_form').off('event_name', {element_Selector}, callback_function) 
} 

cho các đối tượng động. thêm sự kiện khi tạo phần tử và xóa các sự kiện này ngay trước khi hủy phần tử.

Các vấn đề liên quan