Замыкания в цикле for - PullRequest
       14

Замыкания в цикле for

12 голосов
/ 03 февраля 2010

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

Вот основная проблема в упрощенном виде:

function foo(val) {
  alert(val);
}

for (var i = 0; i < 3; i++) {
  $('#button'+i).click(function(){
    foo(i);
  });
}

Естественно, нажатие на любую из трех кнопок выдаст предупреждение 3. Функциональность, которую я хочу, заключается в том, что нажатие на кнопку 1 выдаст предупреждение 1, на кнопке 2 будет 2 и т. Д.

Как мне это сделать?

Ответы [ 5 ]

10 голосов
/ 03 февраля 2010

См. Метод bind .

$('#button'+i).bind('click', {button: i}, function(event) {
  foo(event.data.button);
});

Из документов:

Необязательный параметр eventData обычно не используется.При наличии этого аргумента мы можем передавать дополнительную информацию обработчику.Одним из удобных вариантов использования этого параметра является решение проблем, вызванных замыканиями

6 голосов
/ 03 февраля 2010

Попробуйте этот код:

function foo(val) {
  alert(val);
}

var funMaker = function(k) {
  return function() {
    foo(k);
  };
};

for (var i = 0; i < 3; i++) {
  $('#button'+i).click(funMaker(i));
}

Некоторые важные моменты здесь:

  • JavaScript имеет функциональную область. Если вам нужна новая («более глубокая») область, вам нужно создать функцию для ее удержания.
  • Это решение специфично для Javascript, оно работает с или без jQuery.
  • Решение работает, потому что каждое значение i копируется в новую область видимости как k, а функция, возвращаемая из funMaker, закрывается вокруг k (который не изменяется в цикле), а не вокруг i (что делает).
  • Ваш код не работает, потому что функция, которую вы передаете click, не «владеет» i, она закрывается над i своего создателя, и i изменяется в цикле .
  • Пример мог бы быть написан с funMaker, но я обычно использую такие вспомогательные функции, чтобы сделать вещи более понятными.
  • Аргумент funMaker равен k, но это не имеет значения, он мог бы быть i без каких-либо проблем, так как он существует в области действия функции funMaker.
  • Одно из наиболее четких объяснений модели оценки «Окружающая среда» можно найти в «Структуре и интерпретации компьютерных программ» Sussman & Abelson (http://mitpress.mit.edu/sicp/ полный текст доступен онлайн, не легко читается) - см. раздел 3.2. Поскольку JavaScript на самом деле представляет собой Scheme с синтаксисом C, это объяснение в порядке.

EDIT: исправлены некоторые знаки препинания.

5 голосов
/ 03 февраля 2010

@ Энди решение самое хорошее. Но вы также можете использовать область видимости Javascript, чтобы помочь сохранить значение в вашем закрытии.

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

for (var i = 0; i < 3; i++) {
  (function(){
    var index = i; 
    $('#button'+index).click(function(){
      foo(index);
    });
  })();
}

Поскольку тело цикла является новой областью действия на каждой итерации, переменная индекса дублируется с правильным значением на каждой итерации.

3 голосов
/ 03 февраля 2010

Используйте функцию .each из jquery - я полагаю, вы просматриваете похожие элементы - добавьте клик, используя что-то вроде:

$(element).children(class).each(function(i){
   $(this).click(function(){
      foo(i);
   });
});

Не проверено, но я всегда использую такую ​​структуру, где это возможно.

1 голос
/ 03 февраля 2010

Или просто создайте новую функцию, как вы описываете.Это выглядело бы так:

function foo(val) {
    return function() {
        alert(val);
    }
}

for (var i = 0; i < 3; i++) {
    $('#button'+i).click(foo(i));
}

Я почти уверен, что решение Mehrdad не работает.Когда вы видите людей, копирующих во временную переменную, обычно сохраняется значение «this», которое может быть другим во внутренней дочерней области.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...