Общие сведения о закрытии javascript и использовании памяти
РЕДАКТИРОВАТЬ. Это просто простой пример для демонстрации проблем, которые у меня есть с гораздо более крупной программой. Я бы не использовал этот фактический код для чего-либо:)
Если я запустил это -
<!DOCTYPE html>
<html>
<head>
<script>
function update(amount, win, data)
{
win.innerText = 'Count is ' + amount;
setTimeout(function() { update(amount + 1, win, {data: 'something'})}, 1000);
}
window.onload = function() {
var win = document.getElementById('item');
update(0, win, 0);
}
</script>
</head>
<body>
<div id="item"></div>
</body>
</html>
Вызов setTimeout предположительно создает закрытие, которое фиксирует содержимое параметров функции "update" (сумма, выигрыш, данные). Таким образом, эти переменные сохраняются в памяти до тех пор, пока не будет вызван тайм-аут и не вернется, чтобы они были доступны внутри этого вызова функции...
Но эта функция создает новое замыкание для следующей итерации таймаута... Что будет зафиксировано в этом втором закрытии? Являются ли это только новые копии этих переменных или будут снова захвачены те, которые были частью вызова функции, в новом закрытии?
В основном это закончится из-за нехватки памяти из-за того, что данные в каждом закрытии становятся все больше и больше, или это безопасно и разумно?
Ответы
Ответ 1
В моем понимании, когда создается замыкание, текущий лексический контекст связан с ним. В вашем случае это будет amount, win, data
.
Этот контекст будет использоваться, когда тайм-аут срабатывает, чтобы выполнить закрытие и тем самым снова вызвать функцию update
; этот вызов, хотя и может показаться таким, не является рекурсивным, поскольку предыдущее выполнение update
уже закончилось, и его исходный контекст (динамический, который отличается от лексического) уже освобожден. (Я думаю, это важно заметить, потому что кажется, что вы беспокоитесь о росте стека из-за рекурсии).
Итак, снова update
выполняется второй раз и снова устанавливается тайм-аут и создается закрытие. Это закрытие связано с текущим лексическим контекстом выполнения (который по-прежнему включает только amount, win, data
) и запланировано с помощью таймера. затем update
заканчивается и удаляется из стека. затем снова запускается таймер и обновляется...
Итак, вы не должны беспокоиться о неограниченном росте контекста по двум причинам: во-первых, только лексический контекст связан с закрытием; вызов не является фактически рекурсивным.
Ответ 2
Новое закрытие создается каждый раз, когда вызывается обратный вызов таймаута, как вы правильно говорите. Но как только обратный вызов был выполнен, больше нет ссылок на предыдущее закрытие, поэтому он может быть собран в мусор.