Глобальные переменные
Каждая переменная в Javascript является именованным атрибутом объекта. Например: -
var x = 1;
x добавлено к глобальному объекту. Глобальный объект предоставляется контекстом скрипта и может уже иметь набор атрибутов. Например, в браузере глобальным объектом является окно. Эквивалентом приведенной выше строки в браузере будет: -
window.x = 1;
Локальные переменные
А что если мы изменим это на: -
function fn()
{
var x = 1;
}
Когда вызывается fn
, создается новый объект, называемый контекстом выполнения , также называемый scope (я использую эти термины взаимозаменяемо). x
добавлен в качестве атрибута к этому объекту области. Следовательно, каждый вызов fn
будет получать собственный экземпляр объекта области и, следовательно, собственный экземпляр атрибута x, присоединенный к этому объекту области.
Закрытие
Теперь давайте продолжим: -
function fnSequence()
{
var x = 1;
return function() { return x++; }
}
var fn1 = fnSequence();
var fn2 = fnSequence();
WScript.Echo(fn1())
WScript.Echo(fn2())
WScript.Echo(fn1())
WScript.Echo(fn2())
WScript.Echo(fn1())
WScript.Echo(fn1())
WScript.Echo(fn2())
WScript.Echo(fn2())
Примечание: Замените WScript.Echo
тем, что пишет в стандартный вывод в вашем контексте.
Последовательность, которую вы должны получить: -
1 1 2 2 3 4 3 4
Так что здесь произошло? У нас есть fnSequence
, который инициализирует переменную x
в 1 и возвращает анонимную функцию, которая возвращает значение x
и затем увеличивает его.
Когда эта функция выполняется впервые, создается объект области действия, и к этому объекту области действия добавляется атрибут x
со значением 1. Также в этом же объекте выполнения создается анонимная функция. Каждый объект функции будет иметь атрибут области действия, который указывает на контекст выполнения, в котором он создан. Это создает так называемую цепочку областей действия , к которой мы придем позже. Ссылка на эту функцию возвращается fnSequence
и сохраняется в fn1
.
Обратите внимание, что fn1
теперь указывает на анонимную функцию и что анонимная функция имеет атрибут области действия, указывающий на объект области действия, к которому все еще прикреплен атрибут x
. Это известно как closure
, где содержимое контекста выполнения все еще доступно после завершения функции, для которой оно было создано.
Теперь такая же последовательность происходит при назначении на fn2
. fn2
будет указывать на другую анонимную функцию, созданную в другом контексте выполнения, который был создан при вызове fnSequence
во второй раз.
Цепочка прицела
Что происходит, когда функция, удерживаемая fn1
, выполняется в первый раз? Новый контекст выполнения создается для выполнения анонимной функции. Возвращаемое значение можно найти по идентификатору x
. Объект области действия функции проверяется на наличие атрибута x
, но ни один не найден. Вот тут и начинается цепочка областей действия . Не найдя x
в текущем контексте выполнения, JavaScript берет объект, содержащийся в атрибуте области действия функции, и ищет там x
. Он находит его, поскольку область функций была создана внутри выполнения fnSequence
, извлекает его значение и увеличивает его. Следовательно, выводится 1, а x
в этой области увеличивается до 2.
Теперь, когда fn2
выполняется, он в конечном итоге присоединяется к другому контексту выполнения, атрибут которого x
по-прежнему равен 1. Следовательно, выполнение fn2
также приводит к 1.
Как видите, fn1
и fn2
каждый генерирует свою собственную независимую последовательность чисел.