Нужно ли мне удалять прослушиватели событий перед удалением элементов?

84

Если у меня есть родительский элемент с дочерними элементами, к которым привязаны прослушиватели событий, нужно ли мне удалять эти прослушиватели событий, прежде чем я очищу родительский элемент? (т.е. parent.innerHTML = '';) Могут ли быть утечки памяти, если прослушиватели событий не отвязаны от элемента, если он удален из DOM?

Все рабочие важны
источник

Ответы:

51

Краткий ответ: да

Длинный ответ: большинство браузеров обрабатывают это правильно и сами удаляют эти обработчики. Есть некоторые старые браузеры (IE 6 и 7, если я правильно помню), которые портят это. Да, возможны утечки памяти. Вам не стоит об этом беспокоиться, но вам нужно. Взгляните на этот документ .

Jwueller
источник
В самом деле: хотя большинство современных браузеров не сильно от этого пострадают, IE 7 по-прежнему широко используется. Также ознакомьтесь с шаблонами утечки памяти в JavaScript .
Марсель Корпель
7
Кто-нибудь достаточно осведомлен, чтобы обновить это для текущего рынка браузеров? Или это стоит отдельного вопроса? IE7, как я думал, в значительной степени был прекращен , а IE8 все еще существует. Обрабатывает ли IE8 оставленные прослушиватели событий?
Aidan Miles
30
6 лет спустя, я думаю, IE < 10его можно смело считать устаревшим и не используемым на данный момент никем, кто заходит на другие сайты, кроме Yahoo и AOL. В любом случае, любой, кто использует IE на данном этапе безосновательно, с большей вероятностью станет жертвой телефонного мошенничества в Индии или заразится вирусом, чем в любом случае будет иметь проблемы с обработчиками событий, замедляющими его работу браузера.
Брейден Бест
70

Просто чтобы обновить информацию здесь. Я тестировал различные браузеры, в частности, на предмет утечек памяти для циклически зависимых прослушивателей событий при событиях загрузки iframe.

Используемый код (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 КБ или меньше после запуска тестов. Однако при утечке памяти объем памяти увеличится примерно на 16 000 КБ. Удаление сначала прослушивателя событий всегда приводит к меньшему использованию памяти (без утечек).

Полученные результаты:

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

Вывод: передовые приложения, вероятно, могут обойтись без удаления прослушивателей событий. Но я все равно считаю это хорошей практикой, несмотря на раздражение.

Дуайт
источник