JavaScript: Можете ли вы подставлять переменные в анонимные функции при создании?
Возможный дубликат:
Закрытие Javascript внутри циклов - простой практический пример
Вместо объяснения вопроса приведу пример:
for (var i = 0; i < 100; i ++) {
get_node(i).onclick = function() {
do_something_very_important(i);
}
}
Есть ли способ заменить значение i в функции при создании, а не при выполнении? Благодарю.
2 ответа
Да, вы можете, но это не сработает для приведенного вами примера. У вас будет очень распространенная проблема закрытия в этом for
петля.
Переменные, заключенные в замыкание, совместно используют одну и ту же среду, поэтому к тому времени onclick
обратный вызов называется, for
цикл закончится, и i
переменная будет оставлена, указывая на последнее значение, которое ей было присвоено. В вашем примере do_something_very_important()
функции будет передано значение 100
для каждого узла, что не то, что вы намерены.
Вы можете решить эту проблему с еще большим количеством замыканий, используя фабрику функций:
function makeClickHandler(i) {
return function() {
do_something_very_important(i);
};
}
// ...
for(var i = 0; i < 100; i++) {
get_node(i).onclick = makeClickHandler(i);
}
Это может быть довольно сложной темой, если вы не знакомы с тем, как работают замыкания. Вы можете прочитать следующую статью о Mozilla для краткого введения:
ОБНОВИТЬ:
Вы также можете встроить вышеупомянутую фабрику функций, как @adamse предложил в другом ответе. На самом деле это более распространенный подход, но он практически такой же, как указано выше:
for(var i = 0; i < 100; i++) {
get_node(i).onclick = (function(p) {
return function () {
// we could have used i as a parameter variable as well,
// but we're using p to better illustrate what's happening
do_something_very_important(p);
}
})(i);
}
Любое другое решение состоит в том, чтобы заключить каждую итерацию в ее собственную область, используя самовывоз анонимных функций:
for(var i = 0; i < 100; i++) {
(function (p) {
// we now have a separate closure environment for each
// iteration of the loop
get_node(i).onclick = function() {
do_something_very_important(p);
}
})(i);
}
Да, это работает...
for (var i = 0; i < 100; i++) {
get_node(i).onclick = (function(i) {
return function () {
do_something_very_important(i);
}
})(i);
}