Чи потрібно видаляти прослуховувачі подій перед видаленням елементів?


84

Якщо у мене є батьківський елемент із дітьми, до яких прив’язані прослуховувачі подій, чи потрібно мені видаляти ці прослуховувачі подій, перш ніж очистити батьківський? (тобто, parent.innerHTML = '';) Чи можуть бути витоки пам’яті, якщо прослуховувачі подій не розв’язуються з елементом, якщо його видалити з DOM?

Відповіді:


51

Коротка відповідь: так

Довга відповідь: Більшість браузерів обробляють це правильно і самі видаляють ці обробники. Є деякі старіші браузери (IE 6 і 7, якщо я правильно пам’ятаю), які це псують. Так, можуть бути витоки пам’яті. Вам не слід турбуватися з цього приводу, але вам потрібно. Погляньте на цей документ .


Дійсно: хоча більшість сучасних браузерів не страждають від цього так сильно, IE 7 все ще використовується. Також подивіться на схеми витоків пам'яті в JavaScript .
Марсель Корпель,

7
Хтось достатньо знає, щоб оновити це для поточного ринку браузерів? Або це варто окремого питання? IE7, я думав, був майже відмовлений , тоді як ie8 все ще висить. Чи працює IE8 з прослуховувачами покинутих подій?
Aidan Miles

28
6 років потому, я думаю, IE < 10можна вважати застарілим і не використовувати його тим, хто відвідує інші сайти, окрім Yahoo та AOL. Той, хто на даний момент однозначно використовує IE, швидше за все, стане жертвою індійської телефонної афери або заразиться вірусом, ніж має проблеми з обробниками подій, які загальмують роботу браузера.
Брейден Бест

67

Просто для оновлення інформації тут. Я тестував різні браузери, зокрема щодо витоків пам'яті для циклічно залежних прослуховувачів подій на подіях iframe onload.

Використовуваний код (jsfiddle заважає тестуванню пам’яті, тому для перевірки використовуйте власний сервер):

<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>

Якщо немає витоку пам'яті, використана пам'ять збільшиться приблизно на 1000 кб або менше після запуску тестів. Однак, якщо спостерігається витік пам'яті, пам'ять збільшиться приблизно на 16000 кб. Видалення прослуховувача подій спочатку завжди призводить до зменшення використання пам'яті (відсутність витоків).

Результати:

  • IE6 - витік пам'яті
  • IE7 - витік пам'яті
  • IE8 - відсутність витоків пам'яті
  • IE9 - витік пам'яті (???)
  • IE10 - витік пам'яті (???)
  • IE11 - відсутність витоків пам'яті
  • Edge (20) - відсутність витоків пам'яті
  • Chrome (50) - відсутність витоків пам'яті
  • Firefox (46) - важко сказати, погано витікає, то, можливо, просто неефективний збирач сміття? Завершує додаткові 4 МБ без видимих ​​причин.
  • Opera (36) - відсутність витоків пам'яті
  • Safari (9) - відсутність витоків пам'яті

Висновок: Кровотечі-додатки, ймовірно, можуть уникнути не видалення прослуховувачів подій. Але я все одно вважаю це гарною практикою, незважаючи на досаду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.