Ваш код в порядке, но ответ Энди вводит в заблуждение, потому что он путает цепочку областей действия с контекстом исполнения и, соответственно, расширением, стек вызовов .
Во-первых, setTimeout
функции не не выполняются в глобальной области.Они по-прежнему выполняются в замыкании и могут обращаться к переменным из внешних областей.Это связано с тем, что JavaScript использует static scope ;то есть цепочка областей действия функции определяется в момент, когда функция создается и никогда не изменяется;цепочка областей действия является свойством функции.
контекст выполнения отличается и отличается от цепочки областей действия тем, что он создается во время вызова функции(напрямую - func();
- или в результате вызова браузера, такого как истечение времени ожидания).Контекст выполнения состоит из объекта активации (параметры функции и локальные переменные), ссылки на цепочку областей действия и значения this
.
. стек вызовов может бытьмыслить как массив контекстов выполнения.В нижней части стека находится глобальный контекст выполнения.Каждый раз, когда вызывается функция, ее параметры и значение this
сохраняются в новом «объекте» в стеке.
Если бы мы изменили вашу функцию onRender
, чтобы она просто вызывала себя (this.onRender()
), стек будет быстро переполнен.Это связано с тем, что элемент управления никогда не покидает каждую последующую функцию onRender
, что позволяет вытолкнуть его контекст выполнения из стека вызовов.Вместо этого мы идем все глубже и глубже с каждым onRender
, ожидающим возвращения следующего onRender
, в бесконечном цикле, прерываемом только при переполнении стека.
Однако при вызове setTimeout
управлениенемедленно возвращается и, таким образом, может выйти из функции onRender
, в результате чего ее контекст выполнения будет вытолкнут из стека и отброшен (будет освобожден из памяти GC).
По истечении времени ожидания браузеринициирует вызов onRender
из глобального контекста выполнения;стек вызовов всего два.Появился новый контекст выполнения, который по умолчанию наследовал бы глобальную область видимости как значение this
;вот почему вы должны bind
к вашему Renderer
объекту - но он по-прежнему включает в себя исходную цепочку областей действия, которая была создана при первом определении onRender
.
Как вы видите, вы не создаетебесконечные замыкания по рекурсии, потому что замыкания ( цепочки областей действия ) создаются при определении функции, а не при ее вызове.Кроме того, вы не создаете бесконечные контексты выполнения, потому что они отбрасываются после возврата onRender
.
Мы можем убедиться, что вы не теряете память, протестировав ее .Я позволил этому бежать 500 000 раз и не наблюдал никакой утечки памяти.Обратите внимание, что максимальный размер стека вызовов составляет около 1000 (зависит от браузера) , поэтому он определенно не повторяется.