Подтвердить что ты не робот

Сбор мусора ненужных прослушивателей событий в javascript

Я создаю одностраничный webapp. Это означает, что с течением времени я получаю новые элементы DOM, удаляю ненужные. Например, когда я беру новую форму, я просто заменяю содержимое конкретного div этой формой HTML, а также настраиваю слушателей, уникальных для этих элементов формы. Через некоторое время я заменяю содержимое этой формы новым экземпляром формы (с разными идентификаторами).

Я снова установил слушателей событий для этой новой формы. Теперь предыдущая форма больше не является частью DOM, поэтому элементы DOM должны автоматически собираться с мусором. Я также ожидаю, что функции слушателя, указывающие на элементы, удаленные из DOM, исчезнут.

Однако следующий профиль, собранный из Chrome, показывает, что количество моих слушателей растет с течением времени. Можете ли вы сказать мне, почему это так? Я попытался нажать кнопку "Собрать мусор". Но это профиль, который я получаю. Что-то не так с тем, как я строю свое приложение? Есть ли проблема, и если да, то как мне ее исправить?

Chrome snapshot of my webapp for a few minutes

В случае, если это важно, я использую язык программирования JSP с jquery, jquery-ui и некоторыми другими плагинами. Вот как выглядят динамические фрагменты, которые я добавляю/удаляю на своей странице.

<script>
  $(document).ready(function() {
    $("#unique_id").find(".myFormButton").button().click(
      function() {
        $.ajax({url: "myurl.html",
          success: function(response) {
                console.log(response);
          }
        });
    });
  });
</script>

<div id="unique_id">
    <form>
      <input name="myvar" />
      <button class="myFormButton">Submit</button>
    </form>
</div>

Обновление

Если вы хотите взглянуть на фактический код, вот соответствующая часть. Эта ссылка показывает, что при нажатии кнопки очистки функция clearFindForm вызывается, которая эффективно обновляет содержимое (фрагмент HTML) с использованием запроса ajax и заменяет весь div в этом jsp с выбранным содержимым. Функция refetchContent работает следующим образом: Здесь - ссылка на код в случае, если это помогает в предоставлении лучшего ответа.

function refetchContent(url, replaceTarget) {
  $.ajax({
    url: url,
    data: {},
    type: "GET",
    success: function (response) {
       replaceTarget.replaceWith(response);
    },
    error:   function (response) {
       showErrorMessage("Something went wrong. Please try again.");
    }
  });
}
4b9b3361

Ответ 1

В то время как jQuery очень хорошо удаляет прослушиватели событий для элементов DOM, которые удаляются с помощью его методов (в том числе .html() - просто читайте API: http://api.jquery.com/html/) - он не удаляет прослушиватели событий для элементов DOM, которые могут по-прежнему иметь ссылку на них в отдельном дереве DOM.

Например, если вы делаете что-то вроде этого:

$.ajax({
    ....
})
    .done(function(response,status,jqXHR) {

        //create a detached DOM tree
        form = $(response)

        //add an event listener to the detached tree
        form.find('#someIDInTheResponse').on('submit',function() {

        });

        //add the form to the html
        $('#someID').html(form);
    });

//at some other point in the code
$('#someIDInTheResponse').remove();

Обратите внимание, что в приведенном выше примере, несмотря на то, что вы удалили элемент из DOM, слушатель не будет удален из памяти. Это связано с тем, что элемент все еще существует в памяти в отдельном дереве DOM, доступном через глобальную переменную "form" (это связано с тем, что я не создавал использование "var" для создания начального отдельного дерева DOM в рамках выполняемой функции.... есть некоторые нюансы, и jQuery не может исправить плохой код, он может только сделать это лучше всего.

2 другие вещи:

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

Во-вторых, и несколько менее важно (я приветствую отзывы об этой перспективе с помощью комментариев). Я считаю, что 30 МБ памяти будет довольно высокой базой для веб-приложения. У меня есть довольно интенсивное веб-приложение для Google Maps, которое достигает 30 МБ после часа или около того интенсивного использования, и вы действительно можете заметить, что начинаете замечать его вялость, когда вы это делаете. Господь знает, как это будет выглядеть, если он когда-нибудь достигнет 60 МБ. Я думаю, что IE < 9 станет практически непригодным на данный момент, хотя, как я уже сказал, я приветствую обратную связь с другими людьми по этой идее.

Ответ 2

Интересно, просто ли вы не отключаете/удаляете ранее связанные прослушиватели событий при замене фрагментов?

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

Я хочу сказать, что вы должны посмотреть, выполняете ли вы привязку через "click()" (или с помощью какого-либо другого подхода) к существующим элементам, не отключая сначала существующий прослушиватель событий.

Вы можете взглянуть на ответ moff на этот вопрос, в котором приведен пример щелчка, в частности.

Ответ 3

Я не могу добавить комментарий из-за репутации, но ответить на то, что говорит Адам...

Чтобы обобщить случай, который представляет Адам, это потенциально не имеет никакого отношения к jQuery, проблема может быть в пределах обычного Javascript. Однако вы не представляете достаточно кода для тех, кто действительно подходит к сути проблемы. Ваше использование облачной инкапсуляции может быть совершенно прекрасным, и проблема может быть и в другом месте.

Я бы посоветовал вам искать инструменты для поиска причины утечки памяти (например, визуализация/перемещение всего дерева объектов/области/ссылки/функции и т.д.).

Одной из вещей, которые нужно учитывать при использовании jQuery, являются плагины и глобальные вставки в DOM! Я видел много JS-библиотек, а не только плагины jQuery, которые не предоставляют эсминцы и методы очистки. Худшими нарушителями часто являются вещи с всплывающими окнами и всплывающими окнами, такими как сборщики дат, диалоги и т.д., Которые имеют неприятную привычку добавлять дочерние слои и т.д. В тело, не удаляя их после.

Что-то нужно иметь в виду, если многие люди просто доберутся до того, чтобы сделать вещи конструированными, но не справляются с деструкцией, особенно в JS, потому что они ожидают, что даже в этот день и в возрасте вы будете обслуживать обычные веб-страницы. Вы также должны проверить плагины для методов уничтожения, потому что не все будут подключаться к событию удаления. События также используются беспорядочно в jQuery другими, поэтому обработчик изгоев может останавливать выполнение следующих событий очистки.

В заключение, jQuery - хорошая надежная библиотека, но ее предупреждают, только потому, что кто-то зависит от нее, не означает, что она наследует качество jQuery.

Из любопытства... вы проверили слушателей на document.ready? Возможно, вам нужно вручную скомпоновать их.