Присвоение onclick
innerFunction
создает функцию закрытия, которая сохраняет значение переменных, которые находятся в области действия innerFunction
.Это позволяет коду в innerFunction
ссылаться на переменные над ним и является желательной функцией javascript (чего нет в некоторых других языках).
Те переменные, которые находятся в области действия innerFunction
, включают obj
.Таким образом, пока есть ссылка на это замыкание, переменные в этом замыкании сохраняются.Это одноразовое использование памяти, поэтому оно не накапливается с течением времени и, следовательно, обычно не имеет значения.Но если вы поместите большие данные в одну из этих переменных, то ожидаете, что она будет освобождена, тогда «да», вы будете использовать больше памяти браузера, чем ожидали.
Где это может вызвать проблемы, так этоВ конкретном примере у вас есть циклическая ссылка между JS <==> DOM.В JS вы сохранили (в закрытии функции) ссылку на объект DOM в переменной obj
.В объекте DOM вы сохранили ссылку на код JS и закрытие функции с присвоением атрибута onclick.Некоторые старые браузеры настолько глупы, что даже если вы удалите объект «элемент» из DOM, циклическая ссылка не позволит сборщику мусора когда-либо освободить память.Это не проблема в современных браузерах, поскольку они достаточно умны, чтобы видеть эту циклическую ссылку, и все же освобождают объект, если на него нет внешних ссылок.
В вашем конкретном коде вы фактически не создалиутечка, потому что элемент все еще находится в DOM.bigString - это большой кусок данных, который вы прикрепили к элементу DOM, и он будет оставаться там до тех пор, пока вы не удалите этот атрибут или не удалите объект DOM.Это не утечка, это просто хранилище.
Единственный способ, которым это может стать утечкой в IE6, - это сделать следующее:
<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){
alert("Hi! I will leak");
};
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
// This is used to make the leak significant
};
// called later in your code
function freeObject() {
var obj = document.getElementById("element");
obj.parentNode.removeChild(obj); // remove object from DOM
}
</script>
<button id="element">Click Me</button>
</body>
</html>
Теперь вы удалили объект изDOM (чуть позже в вашем коде) и, вероятно, ожидал освобождения всей памяти, связанной с ним, но циклическая ссылка предотвращает это в IE6.Вы можете обойти это, выполнив следующее:
<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){
alert("Hi! I will leak");
};
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
// This is used to make the leak significant
};
// called later in your code
function freeObject() {
var obj = document.getElementById("element");
obj.onclick=null; // clear handler and closure reference
obj.parentNode.removeChild(obj); // remove object from DOM
}
</script>
<button id="element">Click Me</button>
</body>
</html>
или, если вам не нужна ссылка obj
в замыкании, вы можете полностью обнулить ссылку из замыкания следующим образом:
<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){
alert("Hi! I will leak");
};
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
// This is used to make the leak significant
obj = null; // kills the circular reference, obj will be null if you access if from innerFunction()
};
</script>
<button id="element">Click Me</button>
</body>
</html>
На практике это значимая проблема только в тех случаях, когда использование утечки памяти может быть значительным.
- Если у вас есть большие порции данных, которые вы храните как атрибуты DOM, то утечка одного объекта может привести к утечке большого количества данных.Обычно это решается не сохранением больших порций данных на объектах DOM.Сохраните их в JS, где вы явно управляете временем жизни.
- Если у вас есть долгоживущая страница с большим количеством взаимодействий javascript и операция, которая создает / уничтожает объекты DOM, может выполняться много, много раз.Например, показ слайдов, который выполняется без присмотра, может создавать / уничтожать объекты DOM снова и снова.Даже небольшая утечка памяти на элемент может в конечном итоге привести к возникновению проблемы.В написанном мной слайд-шоу я просто убедился, что не добавлял никаких пользовательских атрибутов в эти конкретные объекты DOM, которые я добавлял / удалял, и что все обработчики событий были удалены перед удалением объекта из DOM.Это должно гарантировать отсутствие циклических ссылок на них.
- Любая операция DOM, которую вы выполняете снова и снова в большом цикле.