Допустим, у нас есть код:
for (var i = 0; i < thumbnails.length; i++) {
thumbnails[i].addEventListener('click', function () {
fullPhoto.src = photos[i];
});
}
В этом коде создается пять обработчиков событий. То есть, пять функций.
У каждой функции есть скрытое свойство, которое хранит ссылку на ту область видимости, где она была создана. Т.е. при выполнении этой функции браузер увидит следующее: “узнай значение i в таком-то месте”. В данном случае - из внешней переменной i.
К тому моменту, как ты нажмешь на картинку и событие вызовется, цикл уже давно отработает. И в переменной i будет храниться число 5. Поэтому любой обработчик событий, когда обратится к переменной i, получит именно число 5.
Поэтому надо сделать так, чтобы обработчик лез не за этой общей переменной i, а хранил ее копию. Чтобы на первой итерации цикла первый обработчик сохранял 0, второй обработчик - 1, и так далее.
Сейчас это решено через let. Просто пишите цикл с let, и на каждой итерации цикла будет создаваться новая копия этой переменной. Т.е. если у вас цикл проходит 10 итераций, то и будет создано 10 “разных” переменных i. А в случае с var - она одна, общая.
Здесь это реализовано через внешнюю функцию. На каждой итерации вызывается функция addThumbnailClickHandler(thumbnails[i], photos[i]);. И каждый раз в памяти сохраняется новая область видимости функции. У вас будет функция-1 addThumbnailClickHandler со значением 0 внутри. Функция-2 addThumbnailClickHandler со значением 1, и так далее. Сколько раз вы вызовете одну и ту же функцию, столько ее копий и будет храниться в памяти. Пока на них есть ссылка, конечно.
Так работают и замыкания. Из некоторой функции outerF() мы возвращаем функцию innerF():
function outerF() {
let a = 10;
function innerF() {
console.log(a);
}
return innerF;
}
let closure = outerF();
outerF() выполняется, возвращает innerF(), и эта функция сохраняется в переменную closure. И вот эта innerF() хранит в себе ссылку на область видимости outerF() с ее всеми значениями. В данном случае она имеет доступ к переменной a. И мы тоже имеем к ней доступ через closure. Вызовем closure() - выполнится та самая innerF(), которая обратится к переменной a из outerF(). Причем вызвать closure() мы можем когда угодно.