Я использую jQuery, и у меня есть странная вещь, которую я не понимаю. У меня есть некоторый код:
for (i = 1; i <= some_number; i++) {
$("#some_button" + i).click(function() {
alert(i);
});
}
"#some_button" как имя говорит - они - некоторые кнопки. При нажатии они должны открыть поле с, он - число, корректное? Но они не делают. Если существует 4 кнопки, они всегда раскрываются "5" (количество кнопок + 1). Почему то, что так?
Это связано с областью видимости JavaScript. Вы можете легко обойти это, введя другую область видимости, добавив функцию и заставив эту функцию вызывать себя и передавать i:
for (var i = 1; i <= some_number; i++) {
(function(j) {
$("#some_button" + j).click(function() {
alert(j);
});
})(i);
}
Это создает закрытие - когда внутренняя функция имеет доступ к области, которая больше не существует, когда функция называется. См. эту статью о MDC для получения дополнительной информации.
РЕДАКТИРОВАТЬ: RE: Самовызывающиеся функции: Самовызывающая функция - это функция, которая вызывает себя анонимно. Вы не создаете его экземпляр и не присваиваете его переменной. Он принимает следующую форму (обратите внимание на открывающие скобки):
(function(args) {
// function body that might modify args
})(args_to_pass_in);
В отношении этого вопроса тело анонимной функции будет иметь следующий вид:
$("#some_button" + j).click(function() {
alert(j);
});
Объединив их вместе, мы получим ответ в первом блоке кода. Функция анонимного самовызова ожидает аргумент с именем j
. Он ищет любой элемент с идентификатором some_button
с целочисленным значением j
в конце (например, some_button1, some_button10). Каждый раз, когда щелкают по одному из этих элементов, отображается значение j
. Предпоследняя строка решения передает значение i
, которое является счетчиком цикла, в котором вызывается анонимная функция самовызова. Если сделать по-другому, это может выглядеть так:
var innerFunction = function(j) {
$("#some_button" + j).click(function() {
alert(j);
});
};
for (var i = 1; i <= some_number; i++) {
innerFunction(i);
}
У вас очень распространенная проблема с замыканием в цикле for
.
Переменные, заключенные в замыкание, имеют общее единое окружение, поэтому к моменту вызова обратного вызова click
цикл уже завершится, а переменная i
будет указывать на последнюю запись.
Это можно решить с помощью еще большего количества замыканий, используя фабрику функций:
function makeOnClickCallback(i) {
return function() {
alert(i);
};
}
var i;
for (i = 0; i < some_number; i++) {
$("#some_button" + i).click(makeOnClickCallback(i));
}
Это может быть довольно сложной темой, если вы не знакомы с тем, как работают замыкания. Вы можете ознакомиться со следующей статьей Mozilla для краткого введения:
Это связано с тем, как замыкания работают в JavaScript. Каждая из 5 создаваемых вами функций, по сути, использует одну и ту же переменную i
. Значение i
внутри вашей функции оценивается не в момент создания функции, а в момент наступления события щелчка, когда значение i
равно 5.
Существуют различные способы обойти это (когда такое поведение не является тем, чего вы хотите). Один из них (если у вас простая функция, как здесь) - использовать конструктор Function вместо литерала функции:
$("#some_button" + i).click(new Function("alert("+i+")");
(function (some_number) {
for (i = 1; i <= some_number; i++) {
$("#some_button" + i).click(function() {
alert(i);
});
}
})(some_number);
Оберните функцию снаружи, потому что для скорости и для того, что я буду продолжать перезагрузку.
Это очень умный код. Настолько умный, что это вопрос на SO. :) Я бы вообще обошел этот вопрос стороной, упростив код, просто чтобы иметь шанс понять его (или чтобы его понял коллега) через полгода. Закрытия имеют свое место, но в данном случае я бы избегал их в пользу более понятного кода.
Возможно, я бы прикрепил ко всем кнопкам одну и ту же функцию, которая получила бы кнопку из события, отделила бы "some_button" от ID и оповестила бы о результате. Это не так красиво, но я гарантирую, что каждый в офисе сможет проследить за этим с первого взгляда.
Потому что в тот момент, когда вы нажимаете на них, i == 5.