Когда вы назначаете функцию обработчику кликов , создается замыкание .
Обычно замыкание формируется, когда вы вкладываете функции, внутренние функции могут ссылаться на переменные, присутствующие во внешних включающих функциях, даже после того, как их родительские функции уже выполнены.
Во время выполнения события щелчка обработчик обращается к последнему значению переменной i
, потому что эта переменная хранится в закрытии.
Как вы заметили,оборачивая функцию обработчика кликов, чтобы принять переменную i
в качестве аргумента, и возвращая другую функцию (в основном создавая другое закрытие), она работает так, как вы ожидаете:
for ( var i = 0; i < 4; i++ ) {
var a = document.createElement( "a" );
a.onclick = (function(j) { // a closure is created
return function () {
alert(j);
}
}(i));
document.getElementById( "foo" ).appendChild( a );
}
Когда вы выполняете итерацию, фактически создайте 4 функции , каждая функция сохраняет ссылку на i
в момент ее создания (путем передачи i
), это значение сохраняется во внешнем замыкании, а внутренняя функция выполняется при событии щелчка
Я использую следующий фрагмент, чтобы объяснить замыкания (и очень базовую концепцию curry ), я думаю, что простой пример может облегчить понимание концепции:
// a function that generates functions to add two numbers
function addGenerator (x) { // closure that stores the first number
return function (y){ // make the addition
return x + y;
};
}
var plusOne = addGenerator(1), // create two number adding functions
addFive = addGenerator(5);
alert(addFive(10)); // 15
alert(plusOne(10)); // 11
Когда запускается событие onclick, вызывается анонимная функция, которая обращается к той же переменной i
, которая использовалась в цикле, и содержит последнее значение i
, то есть 4.
Решение вашей проблемы - использовать функцию, возвращающую функцию:
a.onclick = (function(k) {return function() { alert(k); }; })(i);
Не вдаваясь в подробности, это по существу создает копии переменных экземпляра, оборачивая их в функцию, которая выполняется немедленно и передает обратно функции, которая будет выполнена при щелчке по элементу.
Подумайте об этом так:
function() { alert(i); } // Will expose the latest value of i
(function(I) { return function() { alert(I); }; })(i); // Will pass the current
// value of i and return
// a function that exposes
// i at that time
Итак, во время каждой итерации цикла вы фактически выполняете функцию, которая возвращает функцию с текущим значение переменной.
Что, если вы представите, что у вас есть 4 якоря в вашем цикле, вы создаете 4 отдельные функции, которые можно визуализировать как ..
function() { alert(0); };
function() { alert(1); };
function() { alert(2); };
function() { alert(3); };
Я бы рассмотрел возможность изучения области видимости и замыканий с помощью javascript, как если бы вы пойти по этой дороге и не понимать, что именно происходит, вы можете столкнуться с серьезными проблемами из-за неожиданного поведения.