2010-02-23 2 views
18

Я использую JQuery и делать что-то вроде этогоJavascript утечка памяти

DOM:

<div id="parent"></div> 

JS:

var _doSomeThing = function() 
{ 
    //some codes 
} 

$(function() 
{ 
    // appending div and binding methods to span 
    $('#parent').append('<span>1</span>'); 
    $('#parent').append('<span>2</span>'); 
    $('#parent span').bind('click', _doSomeThing); 
}); 

function _clearDiv() 
{ 
    //clear div 
    $('#parent').html(''); 
} 

//sometime in future, call clear div 
_clearDiv(); 

Теперь мой вопрос, делать обязательные события в DOM, а позже просто удаление элементов из DOM приводит к утечке памяти?

Если да, то как решить эту проблему?

ответ

19

метод JQuery html пытается предотвратить утечку памяти путем удаления обработчиков событий для каких-либо элементов, удаляются в результате вызова .html('') объекта jQuery.

Из 1.4.2 источника

html: function(value) { 
    if (value === undefined) { 
     return this[0] && this[0].nodeType === 1 ? 
     this[0].innerHTML.replace(rinlinejQuery, "") : 
      null; 
    } 
    // See if we can take a shortcut and just use innerHTML 
    // THE RELEVANT PART 
    else if (typeof value === "string" && !rnocache.test(value) && 
     (jQuery.support.leadingWhitespace || !rleadingWhitespace.test(value)) && 
     !wrapMap[ (rtagName.exec(value) || ["", ""])[1].toLowerCase() ]) { 

     value = value.replace(rxhtmlTag, fcloseTag); 

     try { 
      for (var i = 0, l = this.length; i < l; i++) { 
       // Remove element nodes and prevent memory leaks 
       if (this[i].nodeType === 1) { 
        jQuery.cleanData(this[i].getElementsByTagName("*")); 
        this[i].innerHTML = value; 
       } 
      } 

     // If using innerHTML throws an exception, use the fallback method 
     } 
     catch(e) { 
      this.empty().append(value); 
     } 
    } 
    else if (jQuery.isFunction(value)) { 
     this.each(function(i){ 
      var self = jQuery(this), old = self.html(); 
      self.empty().append(function(){ 
       return value.call(this, i, old); 
      }); 
     }); 

    } 
    else { 
     this.empty().append(value); 
    } 
    return this; 
} 

Мы можем видеть, что функция jQuery.cleanData() называется. Вот источник для этого

cleanData: function(elems) { 
    var data, id, cache = jQuery.cache, 
     special = jQuery.event.special, 
     deleteExpando = jQuery.support.deleteExpando; 

    for (var i = 0, elem; (elem = elems[i]) != null; i++) { 
     id = elem[ jQuery.expando ]; 

     if (id) { 
      data = cache[ id ]; 

      if (data.events) { 
       for (var type in data.events) { 
        if (special[ type ]) { 
         jQuery.event.remove(elem, type); 

        } else { 
         removeEvent(elem, type, data.handle); 
        } 
       } 
      } 

      if (deleteExpando) { 
       delete elem[ jQuery.expando ]; 

      } else if (elem.removeAttribute) { 
       elem.removeAttribute(jQuery.expando); 
      } 

      delete cache[ id ]; 
     } 
    } 
} 

Это выглядит в jQuery.cache объект для каких-либо свойств типа событий на события объект свойство объекта данных, относящихся к каждому элементу, которые будут удалены при вызове .html('') и удаляет их.

Чтобы объяснить, как работает стандартное связывание событий, когда функция привязана как обработчик события, созданного на элементе с помощью jQuery, объект данных добавляется как свойство объекта jQuery.cache. Этот объект данных содержит объект свойств событий, который будет иметь свойство, созданное на нем, с именем, соответствующим типу события, к которому вы хотите привязать функцию обработчика события. это свойство будет содержать массив функций, который должен вызываться, когда событие возникает на элементе, поэтому функция обработчика события добавляется в этот массив.Если это первая функция обработчика событий для типа события и элемента, о котором идет речь, функция jQuery.event.handle с вызовом применить (используя этот элемент в качестве контекста, так что this в контексте выполнения функции будет ссылаться на элемент) зарегистрирован в браузер с использованием addEventListener/attachEvent.

При возникновении события функция jQuery.event.handle вызовет все функции в массиве в свойстве объекта свойств событий объекта данных, соответствующего типу события и элементу, на котором было создано событие.

Таким образом, html('') не должно вызывать утечки памяти, поскольку для предотвращения их существует ряд защитных мер.

+0

@ russ Я думаю, что вы правы вчера, я много тестировал и пришел к выводу, что .html (''); также предотвратит утечку памяти, а Внутренний HTML = '' JS вызовет утечку памяти. Я удивляюсь, что jquery делает внутренний список элементов, на которые мы вызвали функцию $() bind() (т. Е. Связали какое-то событие), так что на window.unload мы можем удалить эти события или jquery делает это автоматически для нас. у кого-нибудь есть идея об этом. –

+0

jQuery автоматически связывает функцию для удаления обработчиков событий на 'window.onunload' как часть выполнения внутреннего скрипта. Вы найдете соответствующий код в источнике 1.4.2 непосредственно перед блоком кода Sizzle - http://code.jquery.com/jquery-1.4.2.js :) –

+0

вы правы, даже jquery 1.3.2 версия clear событий, связанных с элементами при выгрузке окна. , поэтому я думаю, что нам не нужно писать наши коды, чтобы очистить связанные события, чтобы избежать утечек памяти. thanx –

2

Не прокомментируйте проблему утечки, но вы можете просто использовать .empty() вместо .html(''). Таким образом, вы очистите innerHTML и удалите все обработчики связанных событий.

1

Вы всегда можете использовать $('#parent span').unbind(); просто чтобы быть уверенным

2

Да, потому что JQuery поддерживает список прилагаемых обработчиков событий, чтобы сделать отсоединив их проще, и для того, чтобы явно отцепить их для вас, когда страница выгружается (который работает над более серьезной утечкой памяти в IE). (Так что Prototype, не может говорить для других libs.) Решение состоит в том, чтобы отцепить их перед удалением элементов (либо напрямую, либо через empty).

0

Поскольку вы постоянно ссылаетесь на $ ('# parent'), вы должны создать ссылку на этот объект в глобальной области, чтобы jQuery не постоянно искал объект для каждого запроса. Выполняя это, вы в основном кэшируете ссылку на объект, который значительно сократит использование памяти.

_parent = $('#parent'); 

...

function(){ _parent.append('<span>1</span>'); } 

Редактировать: Я взял этот наконечник из этой статьи на jQuery Performance Rules

+1

Потрясающе? Профилировали ли вы использование памяти до и после проверки этого требования? – PatrikAkerstrand

+0

У меня было столько утечек памяти в приложении jQuery, что MSIE закроется через 10 минут. После внесения этой корректировки утечки исчезли, и память стабилизировалась в разумной сумме. –

+3

Я бы не назвал это памятью «утечкой», не кэширующей объекты jQuery, а практикой, которая потенциально может привести к плохой производительности, что звучит так, как будто вы испытывали в IE, семейство браузеров, в котором в целом реализовано меньше ('Selectors API',' document.getElementsByClassName'), чем другие браузеры, и поэтому для достижения такого же результата нужно полагаться на реализацию обхода JavaScript DOM. Я бы попытался избежать глобального загрязнения пространства имен, указав переменные, которые вы кешируете объекты jQuery как можно локальнее. –

Смежные вопросы