Tôi có cần xóa trình nghe sự kiện trước khi xóa các phần tử không?


84

Nếu tôi có phần tử cha với phần tử con có người nghe sự kiện bị ràng buộc với chúng, tôi có cần xóa những người nghe sự kiện đó trước khi xóa phần tử gốc không? (ví dụ parent.innerHTML = '';:) Có thể có rò rỉ bộ nhớ nếu các trình xử lý sự kiện không bị hủy liên kết khỏi một phần tử nếu nó bị xóa khỏi DOM không?

Câu trả lời:


51

Câu trả lời ngắn gọn:

Câu trả lời dài: Hầu hết các trình duyệt xử lý điều này một cách chính xác và tự loại bỏ các trình xử lý đó. Có một số trình duyệt cũ hơn (IE 6 và 7, nếu tôi nhớ chính xác) đang làm rối tung điều này. Có, có thể có rò rỉ bộ nhớ. Bạn không cần phải lo lắng về điều này, nhưng bạn cần phải làm. Hãy xem tài liệu này .


Thật vậy: mặc dù hầu hết các trình duyệt hiện tại sẽ không bị như vậy nhưng IE 7 vẫn được sử dụng phổ biến. Ngoài ra, hãy xem các mẫu rò rỉ bộ nhớ trong JavaScript .
Marcel Korpel

7
Có ai đó đủ hiểu biết để cập nhật điều này cho thị trường trình duyệt hiện tại không? Hay đó là giá trị của một câu hỏi riêng biệt? Tôi nghĩ rằng IE7 đã bị loại bỏ khá nhiều , trong khi ie8 vẫn còn tồn tại. IE8 có xử lý trình lắng nghe sự kiện bị bỏ rơi không?
Aidan Miles

27
6 năm sau, tôi nghĩ rằng IE < 10có thể được coi là không dùng nữa và không được sử dụng bởi bất kỳ ai truy cập các trang khác ngoài Yahoo và AOL vào thời điểm này. Dù sao, bất kỳ ai sử dụng IE vào thời điểm này đều có khả năng trở thành nạn nhân của trò lừa đảo qua điện thoại của Ấn Độ hoặc bị nhiễm vi-rút hơn là gặp vấn đề với trình xử lý sự kiện làm chậm trình duyệt của họ.
Braden Best

67

Chỉ cần cập nhật thông tin ở đây. Tôi đã thử nghiệm các trình duyệt khác nhau, đặc biệt để kiểm tra rò rỉ bộ nhớ cho các trình xử lý sự kiện phụ thuộc một cách vòng tròn vào các sự kiện onload iframe.

Mã được sử dụng (jsfiddle can thiệp vào việc kiểm tra bộ nhớ, vì vậy hãy sử dụng máy chủ của riêng bạn để kiểm tra điều này):

<div>
    <label>
        <input id="eventListenerCheckbox" type="checkbox" /> Clear event listener when removing iframe
    </label>
    <div>
        <button id="startTestButton">Start Test</button>
    </div>
</div>

<div>
    <pre id="console"></pre>
</div>

<script>

    (function() {
        var consoleElement = document.getElementById('console');
        window.log = function(text) {
            consoleElement.innerHTML = consoleElement.innerHTML + '<br>' + text;
        };
    }());

    (function() {
        function attachEvent(element, eventName, callback) {
            if (element.attachEvent)
            {
                element.attachEvent(eventName, callback);
            }
            else
            {
                element[eventName] = callback;
            }
        }

        function detachEvent(element, eventName, callback) {
            if (element.detachEvent)
            {
                element.detachEvent(eventName, callback);
            }
            else
            {
                element[eventName] = null;
            }
        }

        var eventListenerCheckbox = document.getElementById('eventListenerCheckbox');
        var startTestButton = document.getElementById('startTestButton');
        var iframe;
        var generatedOnLoadEvent;

        function createOnLoadFunction(iframe) {
            var obj = {
                increment: 0,
                hugeMemory: new Array(100000).join('0') + (new Date().getTime()),
                circularReference: iframe
            };

            return function() {
                // window.log('iframe onload called');
                obj.increment += 1;
                destroy();
            };
        }

        function create() {
            // window.log('create called');
            iframe = document.createElement('iframe');

            generatedOnLoadEvent = createOnLoadFunction(iframe);
            attachEvent(iframe, 'onload', generatedOnLoadEvent);

            document.body.appendChild(iframe);
        }

        function destroy() {
            // window.log('destroy called');
            if (eventListenerCheckbox.checked)
            {
                detachEvent(iframe, 'onload', generatedOnLoadEvent)
            }

            document.body.removeChild(iframe);
            iframe = null;
            generatedOnLoadEvent = null;
        }

        function startTest() {
            var interval = setInterval(function() {
                create();
            }, 100);

            setTimeout(function() {
                clearInterval(interval);
                window.log('test complete');
            }, 10000);
        }

        attachEvent(startTestButton, 'onclick', startTest);
    }());

</script>

Nếu không có rò rỉ bộ nhớ, bộ nhớ đã sử dụng sẽ tăng khoảng 1000kb hoặc ít hơn sau khi chạy thử nghiệm. Tuy nhiên, nếu bị rò rỉ bộ nhớ thì bộ nhớ sẽ tăng thêm khoảng 16.000kb. Việc loại bỏ trình nghe sự kiện trước tiên luôn dẫn đến việc sử dụng bộ nhớ thấp hơn (không bị rò rỉ).

Các kết quả:

  • IE6 - rò rỉ bộ nhớ
  • IE7 - rò rỉ bộ nhớ
  • IE8 - không bị rò rỉ bộ nhớ
  • IE9 - rò rỉ bộ nhớ (???)
  • IE10 - rò rỉ bộ nhớ (???)
  • IE11 - không bị rò rỉ bộ nhớ
  • Cạnh (20) - không bị rò rỉ bộ nhớ
  • Chrome (50) - không bị rò rỉ bộ nhớ
  • Firefox (46) - khó nói, không bị rò rỉ nghiêm trọng, vì vậy có thể chỉ là trình thu gom rác kém hiệu quả? Kết thúc với thêm 4MB mà không có lý do rõ ràng.
  • Opera (36) - không bị rò rỉ bộ nhớ
  • Safari (9) - không bị rò rỉ bộ nhớ

Kết luận: Các ứng dụng cạnh chảy máu có thể thoát khỏi việc không xóa trình nghe sự kiện. Nhưng tôi vẫn coi đó là một thực hành tốt, mặc dù có chút khó chịu.

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.