Ваш "другой подход" правильный, вот как это обычно делается.
А что касается проблемы i
всегда постоянной, вот как работают замыкания!Видите ли, когда вы создаете эту функцию, которая делает что-то с i
(например, function() { alert(i); }
), эта функция, как говорится, 'захватывает' или 'связывает' переменная i
, так что переменная i
не умирает после завершения цикла, но продолжает жить и на нее все еще ссылаются из этой функции.
Чтобы продемонстрировать эту концепцию, рассмотрим следующий код:
var i = 5;
var fn = function() { alert(i); };
fn(); // displays "5"
i = 6;
fn(); // displays "6"
Когда оно написано таким образом, концепция становится немного более очевидной, не так ли?Поскольку вы изменяете переменную в цикле, после завершения цикла переменная сохраняет свое последнее значение (1+steps)
- и это именно то, что видит ваша функция, когда начинает выполняться.
Чтобы обойти это,Вы должны создать другую функцию, которая будет возвращать функцию.Да, я знаю, что-то потрясающее, но терпите меня.Рассмотрим пересмотренную версию моего примера:
function createFn( theArgument )
{
return function() { alert( theArgument ); };
}
var i = 5;
var fn = createFn( i );
fn(); // displays "5"
i = 6;
fn(); // still displays "5". Voila!
Это работает, потому что функция fn
больше не связывает переменную i
.Вместо этого теперь он связывает другую переменную - theArgument
, которая не имеет ничего общего с i
, за исключением того, что они имеют одинаковое значение в момент вызова createFn
.Теперь вы можете изменить i
все, что вы хотите - theArgument
будет непобедимым.
Применяя это к своему коду, вы должны изменить его следующим образом:
function createTimeoutHandler( elemArg, iDivStepsArg )
{
return function() { setOpacity( elemArg, iDivStepsArg ); };
}
for (var i = 1; i <= steps; i++)
{
console.log(i/steps + ', ' + (i/steps) * duration);
setTimeout( createTimeoutHandler( elem, i/steps ), (i/steps) * duration);
}