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);
}
Другие вопросы по тегам