Объем переменных в функциях обратного вызова JavaScript
Я ожидал, что код ниже будет предупреждать "0" и "1", но он предупреждает "2" дважды. Я не понимаю причину. Не знаю, является ли проблема jQuery. Также, пожалуйста, помогите мне изменить заголовок и теги этого сообщения, если они неточны.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
for (var i=0; i<2; i++) {
$.get('http://www.google.com/', function() {
alert(i);
});
}
});
</script>
</head>
<body>
</body>
</html>
Ответы
Ответ 1
Вы делитесь единственной переменной i
среди всех обратных вызовов.
Поскольку Javascript закрывает захват переменных по ссылке, обратные вызовы всегда будут использовать текущее значение i
. Поэтому, когда jQuery вызывает обратные вызовы после выполнения цикла, i
всегда будет 2
.
Вам нужно указать i
как параметр для отдельной функции.
Например:
function sendRequest(i) {
$.get('http://www.google.com/', function() {
alert(i);
});
}
for (var i = 0; i < 2; i++) {
sendRequest(i);
}
Таким образом, каждый обратный вызов будет иметь отдельное закрытие с отдельным параметром i
.
Ответ 2
Альтернатива ответу SLaks
$(function() {
for (var i=0; i<2; i++) {
$.get('http://www.google.com/', function(i) {
return function() { alert(i); }
}(i));
}
});
Ответ 3
Что происходит здесь, ваш запрос AJAX $.get
завершается после завершения цикла. Из-за этого i
заканчивается как конечная переменная, которую он установил, когда итерации завершены, будучи 2. Это просто странный JavaScript-код и не имеет ничего общего с jQuery.
Одна вещь, которую вы можете сделать, заключается в асинхронной очереди этих вызовов, так что итерация останавливается до тех пор, пока не будет завершен текущий запрос AJAX. Если вы не хотите этого делать, вы можете захватить переменную i
в закрытии function
на каждой итерации.
Что-то вроде этого:
for ( var i = 0; i < 2; i++ )
(function(iter){
$.get('http://www.google.com/', function(){
alert( iter );
});
})(i); // Capture i
Ответ 4
Альтернативное решение для этого - принять ваш обратный вызов и буквально сделать его именованной функцией.
Почему я хочу это сделать?
Если функция выполняет что-то, где переменная должна обладать новой областью, то, скорее всего, анонимная функция требует выхода в новую функцию. Это также гарантирует, что дополнительная сложность не будет введена в ваш код, если вам придется копировать переменные или завершать обратные вызовы. Вы код остаетесь простым и самоописательным.
Пример:
function getGoogleAndAlertIfSuccess(attemptNumber) {
$.get('http://www.google.com/', function() {
alert(attemptNumber);
});
}
function testGoogle() {
for (var i=0; i<2; i++) {
getGoogleAndAlertIfSuccess(i);
}
}
Ответ 5
Похоже, что вы создали закрытие внутри цикла. Справочник разработчиков Mozilla имеет хороший раздел об этом.