Замыкания в петле цикла
Закрытие в цикле вызывает у меня проблемы. Я думаю, что мне нужно сделать еще одну функцию, которая возвращает функцию для решения проблемы, но я не могу заставить ее работать с моим кодом jQuery.
Вот основная проблема в упрощенной форме:
function foo(val) {
alert(val);
}
for (var i = 0; i < 3; i++) {
$('#button'+i).click(function(){
foo(i);
});
}
Естественно, нажав на любую из трех кнопок, вы получите предупреждение 3. Функциональность, которую я хочу, - это то, что нажатие кнопки 1 даст предупреждение 1, кнопка 2 скажет 2 и т.д.
Как я могу сделать это?
Ответы
Ответ 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", которое может быть различным во внутренней области содержимого.