Ваша аргументная функция setTimeout
объявлена для принятия одного параметра, i
.Когда setTimeout
вызывает функцию без аргументов, как это происходит, параметр, таким образом, устанавливается на undefined
.
Казалось бы, это будет немного лучше, поскольку он больше не затеняет внешнее i
переменная, на которую вы первоначально пытались сослаться ...
setTimeout(function() {
console.log(i);
}, 2000)
... но если вы запустите ее, вы обнаружите, что она печатает 10
5 раз, потому что каждая созданная вами функция ссылается на одну и ту же i
переменная, значение которой будет 10
, когда условие выхода из цикла становится true
и оно завершается.
Создание замыкания , которое содержит значение i
в том виде, как оно былово время цикла, в котором была создана каждая setTimout
функция аргумента, все получится:
setTimeout((function(i) {
return function() {
console.log(i);
}
})(i), 2000)
Переименование аргумента в выражение немедленного вызова функции , которое мы только что использовали, может помочь сделать вещиclearer:
setTimeout((function(loopIndex) {
return function() {
console.log(loopIndex);
}
})(i), 2000)
Мы:
- Создаем функцию, которая принимает один аргумент и возвращает другую функцию.
- Немедленно вызывая «внешнюю» функцию, паспойте ему текущее значение
i
(которое не является объектом, поэтому эффективно передается по значению). - Функция "external" возвращает функцию "inner", которая передается в качестве аргумента
setTimeout
.
Это работает, потому что:
При создании функции в JavaScript создается новая область для хранения переменных аргумента функции илюбые другие переменные, объявленные в нем с использованием ключевого слова var
.Думайте об этом как о невидимом объекте со свойствами, соответствующими именам переменных.
Все функции содержат ссылку на область, в которой они были определены, как часть их цепочки областей действия .Они по-прежнему имеют доступ к этой области, даже если она больше не «активна» (например, когда возвращается функция, для которой она была создана).«Внутренняя» функция была создана в области видимости, которая содержала переменную loopIndex
, установленную на значение i
во время вызова IIFE.Поэтому, когда вы пытаетесь сослаться на переменную с именем loopIndex
изнутри внутренней функции, она сначала проверяет свою собственную область (и не находит там loopIndex
) , а затем начинает подниматься по цепочке областей действия -во-первых, он проверяет область, в которой он был определен, в котором содержит переменную loopIndex
, значение которой передается в console.log()
.
Вот и все замыкание - функция, которая имеет доступ к области, в которой она была определена, даже если функция, для которой была создана область, завершила выполнение.