Ответ 1
Несмотря на то, что при написании больших проектов с открытым кодом JavaScript необходимо избегать особых проблем с браузером, при использовании библиотеки, такой как jQuery, следует предположить, что дизайн библиотеки помогает избежать этих проблем. Однако, учитывая утечку памяти, довольно сложно отследить, и каждая другая версия конкретного браузера может вести себя по-другому - гораздо лучше знать, как вообще избежать утечек памяти, чем быть конкретным:
- Если ваш код повторяется много раз, убедитесь, что переменные, которые вы используете, могут быть отброшены сборкой мусора и не привязаны к ссылкам закрытия.
- Если ваш код имеет дело с большими структурами данных, убедитесь, что у вас есть способ удаления или аннулирования данных.
- Если ваш код создает много объектов, функций и прослушивателей событий, всегда лучше включать и некоторые деконструктивные коды.
- Старайтесь избегать прикрепления объектов или функций javascript к элементам непосредственно в качестве атрибута - т.е.
element.onclick = function(){}
. - Если вы сомневаетесь, всегда убирайте код, когда ваш код завершен.
Вы, кажется, полагаете, что это способ вызова функции, которая будет влиять на утечку, однако всегда гораздо более вероятно, что это будет содержание тех функций, которые могут вызвать проблему.
С вашим кодом выше, мои единственные предложения:
-
Всякий раз, когда используются прослушиватели событий, попробуйте найти способ повторного использования функций, а не создавать по одному для каждого элемента. Это может быть достигнуто с помощью делегирование событий (захват события на родительском родителе и делегирование реакции на
event.target
) или кодирование единственной общей функции для относительного отношения к вашим элементам, чаще всего относительноthis
или$(this)
. -
При необходимости создания многих обработчиков событий обычно лучше хранить эти прослушиватели событий как именованные функции, чтобы вы могли удалить их снова, когда закончите. Это означало бы избежать использования анонимных функций, как вы это делаете. Однако, если вы знаете, что это только ваш код, связанный с DOM, вы можете отказаться от использования
$(elements).unbind('click')
, чтобы удалить все обработчики кликов (анонимные или нет), применяемые с помощью jQuery для выбранных элементов. Однако, если вы используете этот последний метод, определенно лучше использовать способность jQuery event namespacing - чтобы вы знали, что вы удаляете только свои события. т.е.$(elements).unbind('click.my_app');
. Это, очевидно, означает, что вам нужно привязывать события, используя$(elements).bind('click.my_app', function(){...});
более конкретно:
автоматический вызов анонимной функции
(function(){
/*
running an anonymous function this way will never cause a memory
leak because memory leaks (at least the ones we have control over)
require a variable reference getting caught in memory with the
JavaScript runtime still believing that the variable is in use,
when it isn't - meaning that it never gets garbage collected.
This construction has nothing to reference it, and so will be
forgotten the second it has been evaluated.
*/
})();
добавление анонимного прослушивателя событий с помощью jQuery:
var really_large_variable = {/*Imagine lots of data here*/};
$(element).click(function(){
/*
Whilst I will admit not having investigated to see how jQuery
handles its event listeners onunload, I doubt if it is auto-
matically unbinding them. This is because for most code they
wont cause a problem, especially if only a few are in use. For
larger projects though it is a good idea to create some beforeunload
or unload handlers that delete data and unbind any event handling.
The reason for this is not to protect against the reference of the
function itself, but to make sure the references the function keeps
alive are removed. This is all down to how JS scope works, if you
have never read up on JavaScript scope... I suggest you do so.
As an example however, this anonymous function has access to the
`really_large_variable` above - and will prevent any garbage collection
system from deleting the data contained in `really_large_variable`
even if this function or any other code never makes use of it.
When the page unloads you would hope that the browser would be able
to know to clear the memory involved, but you can't be 100% certain
it will *(especially the likes of IE6/7)* - so it is always best
to either make sure you set the contents of `really_large_variable` to null
or make sure you remove your references to your closures/event listeners.
*/
});
tearDowns и деконструкция
Я сосредоточил внимание на моих объяснениях - на том, когда страница больше не требуется, и пользователь перемещается. Однако вышеупомянутое становится еще более актуальным в современном мире ajaxed контента и высокодинамичных интерфейсов; GUI, которые постоянно создают и уничтожают элементы.
Если вы создаете динамическое приложение javascript, я не могу подчеркнуть, насколько важно иметь конструкторы с методами .tearDown
или .deconstruct
, которые выполняются, когда код больше не требуется. Они должны проходить через большие конструкции пользовательских объектов и сворачивать их содержимое, а также удалять прослушиватели событий и элементы, которые были динамически созданы и больше не используются. Вы также должны использовать метод jQuery empty
перед заменой содержимого элемента - это может быть лучше объяснено в их словах:
Чтобы избежать утечек памяти, jQuery удаляет из дочерних элементов другие конструкторы, такие как данные и обработчики событий, перед удалением самих элементов.
Если вы хотите удалить элементы, не разрушая их данные или обработчики событий (чтобы их можно было повторно добавить позже), используйте .detach().
Не только кодирование с помощью методов tearDown заставляет вас делать это более аккуратно (т.е. следить за тем, чтобы связанный код, события и элементы с именами помещались вместе), обычно это означает, что вы создаете код более модульным способом; что, безусловно, намного лучше для будущего тестирования вашего приложения, для чтения и для всех, кто может взять ваш проект на более поздний срок.