Это означает, что ваш код использовал ссылочную переменную объекта, которая была установлена в нуль (т. е. она не ссылалась на экземпляр фактического объекта).
Чтобы предотвратить ошибку, объекты, которые могут быть пустыми, должны быть протестированы для null перед тем, как использовать.
if (myvar != null)
{
// Go ahead and use myvar
myvar.property = ...
}
else
{
// Whoops! myvar is null and cannot be used without first
// assigning it to an instance reference
// Attempting to use myvar here will result in NullReferenceException
}
Функции обработчика событий подчиняются той же коллекции мусора, что и другие переменные. Это означает, что они будут удалены из памяти, когда интерпретатор определит, что нет возможности получить ссылку на эту функцию. Однако простое удаление узла не гарантирует сбор мусора. Например, возьмите этот узел и связанный с ним обработчик событий
var node = document.getElementById('test');
node.onclick = function() { alert('hai') };
Теперь удалите узел из DOM
node.parentNode.removeChild(node);
Итак, node
больше не будет отображаться на вашем веб-сайте , но он явно все еще существует в памяти, как и обработчик событий
node.onclick(); //alerts hai
Пока ссылка на node
все еще доступна, это связанные свойства (из которых onclick
является одним) будет оставаться неповрежденным.
Теперь давайте попробуем его, не создавая оборванную переменную
document.getElementById('test').onclick = function() { alert('hai'); }
document.getElementById('test').parentNode.removeChild(document.getElementById('test'));
. В этом случае, похоже, нет никакого другого способа доступа к узлу DOM #test, поэтому когда цикл сбора мусора запускается, обработчик onclick
должен быть удален из памяти.
Но это очень простой случай. Использование Javascript закрытий может значительно усложнить определение собираемости мусора. Давайте попробуем привязать несколько более сложную функцию обработчика событий к onclick
document.getElementById('test').onclick = function() {
var i = 0;
setInterval(function() {
console.log(i++);
}, 1000);
this.parentNode.removeChild(this);
};
. Поэтому, когда вы нажимаете на #test, элемент будет немедленно удален, но через секунду, а затем каждую секунду, вы будет видеть увеличенный номер, напечатанный на вашей консоли. Узел удаляется, и дальнейшая ссылка на него невозможна, но, похоже, его части остаются. В этом случае сама функция обработчика событий, скорее всего, не сохраняется в памяти, а область ее создания.
Итак, ответ, который я предполагаю; это зависит. Если есть болтающиеся, доступные ссылки на удаленные узлы DOM, связанные с ними обработчики событий будут по-прежнему находиться в памяти вместе с остальными их свойствами. Даже если это не так, область, созданная функциями обработчика событий, все еще может использоваться и в памяти.
В большинстве случаев (и с радостью игнорируя IE6) лучше всего доверять сборщику мусора, чтобы выполнять свою работу, Javascript не является C в конце концов. Однако в таких случаях, как в последнем примере, важно написать некоторые деструкторные функции для неявной функции закрытия.
Возможно, вам придется удалить эти обработчики событий.
Утечки памяти Javascript после разгрузки веб-страницы
В нашем коде, который не основан на jQuery, но некоторые прототипы deviant, у нас есть инициализаторы и деструкторы в наших классах. Мы обнаружили, что абсолютно необходимо удалить обработчики событий из объектов DOM, когда мы уничтожаем не только наше приложение, но и отдельные виджеты во время выполнения.
В противном случае мы закончим утечку памяти в IE.
Удивительно легко получить утечки памяти в IE - даже когда мы выгружаем страницу, мы должны быть уверены, что приложение «выключается» чисто, убирает все - или процесс IE будет расти с течением времени.
Изменить : Чтобы сделать это правильно, у нас есть наблюдатель событий на window
для события unload
. Когда это событие приходит, наша цепочка деструкторов вызывается для правильной очистки каждого объекта.
И некоторый пример кода:
/**
* @constructs
*/
initialize: function () {
// call superclass
MyCompany.Control.prototype.initialize.apply(this, arguments);
this.register(MyCompany.Events.ID_CHANGED, this.onIdChanged);
this.register(MyCompany.Events.FLASHMAPSV_UPDATE, this.onFlashmapSvUpdate);
},
destroy: function () {
if (this.overMap) {
this.overMap.destroy();
}
this.unregister(MyCompany.Events.ID_CHANGED, this.onIdChanged);
this.unregister(MyCompany.Events.FLASHMAPSV_UPDATE, this.onFlashmapSvUpdate);
// call superclass
MyCompany.Control.prototype.destroy.apply(this, arguments);
},
Документация метода jQuery empty()
отвечает на мой вопрос и дает мне решение моей проблемы. В нем говорится:
Чтобы избежать утечек памяти, jQuery удаляет из дочерних элементов другие конструкторы, такие как данные и обработчики событий, перед удалением самих элементов.
blockquote>So : 1) если мы не сделали этого явно, мы получили бы утечки памяти, а 2) с помощью
empty()
, я могу избежать этого.Поэтому я должен сделать это:
formSection.empty(); formSection.html(newContents);
Мне все еще не ясно, будет ли
.html()
позаботиться об этом сам по себе, но одна дополнительная строка, чтобы быть уверенной, не беспокоит меня.
Я хотел знать себя так после небольшого теста, я думаю, что ответ да.
removeEvent вызывается, когда вы .remove () что-то из DOM.
< / blockquote>Если вы хотите увидеть его сами, вы можете попробовать это и выполнить код, установив точку останова. (Я использовал jquery 1.8.1)
Добавить новый div first:
blockquote>$('body').append('<div id="test"></div>')
Проверить$.cache
, чтобы убедиться, что к нему не добавлены события. (это должен быть последний объект). Присоедините к нему событие click:$('#test').on('click',function(e) {console.log("clicked")});
Проверьте его и посмотрите новый объект в$.cache
:$('#test').click()
Удалите его, и вы увидите, что объект в$.cache
также исчез:$('#test').remove()