Подробнее про замыкания в JavaScript
Замыкание - одно из мощных выразительных средств javascript, которым часто пренебрегают, и даже не советуют употреблять.
Действительно, замыкания создать очень легко, даже ненароком, и они могут приводить к проблемам. Но на самом деле они очень удобны, просто нужно понимать, что реально происходит.
Простое описаниеЕсли говорить просто, то замыкания - это внутренние функции. Ведь javascript разрешает создавать функции по ходу выполнения скрипта. И эти функции имеют доступ к переменным внешней функции.
В этом примере создается внутренняя функция func , изнутри которой доступны как локальные переменные, так и переменные внешней функции outer :
function outer() { var outerVar; var func = function () { var innerVar ... x = innerVar + outerVar } return func } |
Когда заканчивает работать функция outer , внутренняя функция func остается жить, ее можно запускать в другом месте кода.
Получается, что при запуске func используется переменная уже отработавшей функции outer , т.е самим фактом своего существования, func замыкает на себя переменные внешней функции (а точнее - всех внешних функций).
Наиболее часто замыкания применяются для назначения функций-обработчиков событий:
Здесь динамически созданный обработчик события handler использует targetId из внешней функции для доступа к элементу.
Если Вы хотите углубиться поглубже и разбираться подольше..
На самом деле происходящее в интерпретаторе Javascript гораздо сложнее и содержит куда больше деталей, чем здесь описано...
Но чтобы понять и использовать замыкания, достаточно понять внутренний механизм работы функций, хотя бы и в таком, местами упрощенном виде...
[]Каждое выполнение функции хранит все переменные в специальном объекте с кодовым именем [], который нельзя получить в явном виде, но он есть .
Каждый вызов var ... - всего лишь создает новое свойство этого объекта, а любое упоминание переменной - первым делом ищется в свойствах этого объекта.
Такова внутренняя структура "области видимости" - обычный объект. Все изменения локальных переменных являются изменениями свойств этого неявного объекта.
Обычно после того, как функция закончила выполнение, ее область видимости [] , т.е весь набор локальных переменных убивается.
Общий поток выполнения выглядит так:
Кстати, для кода вне функции(и вообще глобальных переменных) роль объекта-контейнера [] выполняет объект window .
Область видимости вложенной функцииКогда одна функция создается внутри другой, то для нее создается ссылка на объект с локальными переменными [] внешней функции.
Именно за счет этого из внутренней функции можно получить переменные внешней функции - через ссылку на ее [] . Сначала ищем у себя, затем - во внешнем [] - и так далее по цепочке до самого объекта window .
Замыкание - это когда объект локальных переменных [] внешней функции остается жить после ее завершения.
Внутренняя функция может обратиться к нему в любой момент и получить переменную внешней функции.
Например, разберем работу функции, которая устанавливает обработчики событий:
function addHideHandler(sourceId, targetId) { // создан объект [] со свойствами sourceId, targetId // записать в [] свойство sourceNode var sourceNode = document.getElementById (sourceId) // записать в [] свойство handler var handler = function () { var targetNode = document.getElementById (targetId) targetNode.style .display = ‘none’ } sourceNode.onclick = handler // функция закончила выполнение // (***) и тут - самое интересное! } |
При запуске функции все происходит стандартно:
Но в самом конце - внутренняя функция присваивается sourceNode.onclick . Внешняя функция закончила свою работу, но внутренняя - может запуститься когда-нибудь потом.
Интерпретатор javascript не проводит анализ - понадобятся ли внутренней функции переменные из внешней, и какие переменные могут быть нужны.
Вместо этого он просто оставляет весь [] внешней функции в живых.
Чтобы когда внутренняя функция запустится, если она вдруг не найдет какую-либо переменную в своем [] - она могла обратиться к [] внешней функции и нашла бы ее там.
Если внешняя функция была создана внутри еще одной (еще более внешней) функции - то в цепочку добавляется еще один консервированный [] и так - до глобальной области window .
Пример на пониманиеВ этом примере внешняя функция makeShout () создает внутреннюю shout ().
function makeShout() { var phrase = "Превед!" var shout = function () { alert (phrase) } phrase = "Готово!" return shout } shout = makeShout() // что выдаст? shout() |
Функция shout () на правах внутренней функции имеет доступ к переменной phrase . Какое значение она выведет - первое или второе?
А вот - подробное описание происходящего в недрах javascript:
То есть, внутренняя функция получает последнее значение внешних переменных.
Пример ошибочного использованияФункция addEvents принимает массив div "ов и ставит каждому вывод своего номера на onclick .
С вопроса "Почему это не работает?" люди обычно начинают изучение замыканий.