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

Обработчики событий, закрытие и сбор мусора в Javascript

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

SomeClass.prototype.someMethod= function() {
    var that= this
    this.$div2.click(function() {
        that.someMethod2();
    });
}

И скажем, что это. $div2 добавляется к другому div this. $div1. Если я позвоню

this.$div1.remove();

и позже теряет ссылку на мой экземпляр SomeClass, экземпляр SomeClass получает сбор мусора? А как насчет элемента HTML this. $Div2? это. $div2 не будет внутри DOM, потому что он добавлен к этому. $div1.

Я спрашиваю об этом, потому что обработчик событий в этом. $div2 может содержать ссылку на элемент HTML this. $div2, а также сохраняет ссылку на экземпляр SomeClass через закрытие из-за переменной "that".

Так что я должен заботиться о том, чтобы правильно удалить все события и элементы HTML, подобные этому? Или просто удалить элемент "root" (этот. $Div1) решает проблему?

4b9b3361

Ответ 1

this.$div2 добавляется к this.$div1. Если я вызываю this.$div1.remove(); и позже потеряю ссылку моего экземпляра SomeClass, экземпляр SomeClass получает сбор мусора?

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

А как насчет HTML-элемента this.$div2? this.$div2 не будет внутри DOM, поскольку он добавляется к this.$div1.

Не имеет значения, привязан ли он к DOM. Если некоторые ссылки на не коллекционируемые объекты $div1, он также может получить доступ к своему дочернему элементу node $div2 и к одному обработчику событий, поэтому экземпляр, на который ссылается обработчик, не будет доступен для коллекционирования.

Я спрашиваю об этом, потому что обработчик событий в this.$div2 может содержать ссылку на элемент HTML this.$div2, а также сохраняет ссылку на экземпляр SomeClass через закрытие из-за переменной "that".

Это круговая ссылка и должна хорошо обрабатываться двигателями (когда ни один из объектов внутри круга не ссылается извне, он может быть собран). Однако (старые?) Internet Explorers не могут этого сделать, когда объект DOM участвует в круге.

По этой причине .remove jQuery method (code) внутренне вызывает метод (внутренний) cleanData, который отделяет всех прослушивателей событий.

Так что я должен заботиться о том, чтобы правильно удалить все события и элементы HTML, подобные этому? Или просто удалить элемент "root" (этот. $Div1) решает проблему?

Да, вызов remove в обертке jQuery автоматически удаляет все события (из всех дочерних элементов) и узлы DOM.

Ответ 2

Должен ли я заботиться о правильном удалении всех событий и элементов HTML как это?

Короткий ответ: Нет! по крайней мере, в 99% случаев, это не имеет никакого значения, потому что память, используемая одним элементом DOM, тривиальна по сравнению с общей памятью, используемой веб-страницей.

Однако всегда рекомендуется извлекать память, используемую для удаления ненужных объектов, но вы не можете сказать, что GC определенно освободит память, используемую элементами, потому что сбор мусора полностью зависит от браузера! В теории GC должен только срабатывать, когда нет ссылок на элемент DOM, по крайней мере, как Chrome работает, но на таких языках, как JavaScript, вы не указывайте явным образом время выполнения, которое вы выполнили с объектом, все так быстро запутывается в JavaScript: функция может передать объект другим функциям, объект может быть сохранен как член внутри еще одного объекта, объект может получить ссылку через закрытие и т.д., поэтому он полностью зависит от браузера, как и что собирать!

В вашем случае удаление div1 освобождает html-документ, и элемент не будет отображаться в представлении, на самом деле метод jQuery remove позволяет удалить все события, свойства expando и дочерние элементы, связанные с элементом вместе с самим элементом, однако вы сохраняете ссылку div1 и div2 в еще одном объекте, делающем оба элемента DOM элементами Orphan! удаление переменной экземпляра SomeClass освобождает все ссылки на элементы DOM, что делает их кандидатом для сбора мусора, но здесь возникает сложная переменная that, которая заставляет элемент DOM ссылаться на экземпляр SomeClass через clusure! Эта проблема известна как Circular Reference в IE:

Объекты JavaScript и элементы DOM, которые хранят ссылки на один еще одна причина, по которой сборщик мусора Internet Explorers не восстанавливает памяти, что приводит к утечкам памяти.

enter image description here

Подробнее об этом вы можете прочитать здесь

Эта конкретная утечка в основном имеет исторический интерес IE < 8, но хорошим примером нарушения круговых связей является избежание использования переменной that, вместо этого используйте proxy или delegate, чтобы изменить контекст обработчика события в определенном контексте.

ECMA 5-й метод связывания перестает полезно изменять контексты, когда он приходит к обработчикам событий DOM, здесь простой обработчик, основанный на вашем коде без использования переменной закрытие:

this.$div2.click((function() {
        this.someMethod2();
    }).bind(this));

Ответ 3

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

для фиксированных элементов, если вам нужно событие, используйте эти две функции; первый вызванный в конструкторе, второй в деструкторе.

on_Events: function() {
   $('your_form').on('event_name', {element_Selector}, callback_function)
},
off_Events: function() {
   $('your_form').off('event_name', {element_Selector}, callback_function)
}

для динамических объектов. добавлять события при создании элемента и удалять эти события непосредственно перед уничтожением элемента.