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

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

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

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

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

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

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

Как я могу сделать это?

4b9b3361

Ответ 1

См. метод bind.

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

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

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

Ответ 2

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

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 inlined, но я обычно использую такие вспомогательные функции, чтобы сделать вещи более ясными.
  • Аргумент funMaker равен k, но это не имеет никакого значения, он мог бы быть i без каких-либо проблем, поскольку он существует в области функции funMaker.
  • Одно из самых ясных объяснений модели оценки "Окружающая среда" найдено в "Структура и интерпретация компьютерных программ", Суссман и Абельсон (http://mitpress.mit.edu/sicp/ полный текст доступен в Интернете, а не просто читать) - см. раздел 3.2. Поскольку JavaScript действительно схема с синтаксисом Си, это объяснение в порядке.

EDIT: Исправлена ​​некоторая пунктуация.

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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