Подтвердить что ты не робот

Как работает функция в цикле (которая возвращает другую функцию)?

Я пытаюсь назначить функцию для события onclick динамически созданного тега "a" в JavaScript. Все теги создаются в цикле следующим образом:

for ( var i = 0; i < 4; i++ )
{
  var a = document.createElement( "a" );
  a.onclick = function( ) { alert( i ) };
  document.getElementById( "foo" ).appendChild( a );
}

Предупреждающее значение для всех четырех ссылок всегда "4". Довольно очевидно. Когда googling я наткнулся на сообщение, которое показывает следующий фрагмент кода:

a.onclick = (function(p, d) {
return function(){ show_photo(p, d) }
})(path, description);

Мне удалось настроить его для моих нужд и я получил предупреждение (i), чтобы работать правильно, но я буду признателен, если кто-нибудь сможет объяснить, что именно делает этот код.

4b9b3361

Ответ 1

Когда вы назначаете функцию обработчику кликов, создается closure.

Как правило, замыкание происходит, когда вы вставляете функции, внутренние функции могут ссылаться на переменные, присутствующие в их внешних закрывающих функциях, даже после того, как их родительские функции уже выполнены.

В момент выполнения события click обработчик ссылается на последнее значение, которое имела переменная 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

Ответ 2

Не вдаваясь в подробности, это существенно создает копии переменных экземпляра, обертывая их в функцию, которая выполняется немедленно и передает обратно функции, которая будет выполняться при нажатии элемента.

Подумайте об этом так:

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, как если бы вы пошли по этому пути и не понимаете, что именно происходит, вы можете столкнуться с серьезными проблемами из-за неожиданного поведения.

Ответ 3

Когда запускается событие onclick, вызывается анонимная функция и относится к той же переменной i, которая была использована в цикле, и она содержит последнее значение i, которое равно 4.

Решение вашей проблемы заключается в использовании функции, возвращающей функцию:

a.onclick = (function(k) {return function() { alert(k); }; })(i);