2010-05-17 3 views
19

Я ожидал, что код ниже будет предупреждать «0» и «1», но он предупреждает «2» дважды. Я не понимаю причину. Не знаю, является ли проблема jQuery. Также, пожалуйста, помогите мне изменить заголовок и теги этого сообщения, если они неточны.объем переменных в функциях обратного вызова JavaScript

<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> 
+2

@chaos: прямо под "Не делать HTML с регулярным выражением", я думаю. ;) – Tomalak

+2

* (без обид) * [Закрытие JavaScript для чайников] (http://blog.morrisjohns.com/javascript_closures_for_dummies.html) Пример 5 –

+0

Трудно выбрать только один для закрытия: http://stackoverflow.com/ вопросы/1734749/ http: // stackoverflow.ком/вопросы/643542/ http://stackoverflow.com/questions/1582634/ http://stackoverflow.com/questions/1331769/ http://stackoverflow.com/questions/1552941/ http://stackoverflow.com/questions/750486/ http://stackoverflow.com/questions/933343/ http://stackoverflow.com/questions/1579978/ http://stackoverflow.com/ вопросы/1413916/ http://stackoverflow.com/questions/2808471/ – CMS

ответ

38

Вы разделяя единственную 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 параметра.

0

Похоже, вы создали закрытие внутри своей петли. Справочник разработчиков Mozilla содержит good section об этом.

1

Что происходит здесь, ваш запрос 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 
13

Альтернатива ответ SLaks'

$(function() { 
    for (var i=0; i<2; i++) { 
     $.get('http://www.google.com/', function(i) { 
      return function() { alert(i); } 
     }(i)); 
    } 
}); 
+1

@ RTF Нет, это то же самое, просто выраженное по-разному. Кроме того, такие вещи, скорее всего, являются последним местом, где ваша производительность идет вниз, поэтому используйте его, потому что вам это нравится лучше, а не потому, что вы считаете, что это может быть быстрее. (Чтобы быть абсолютно честным, это не * точно * то же самое. Он создает один дополнительный функциональный объект для каждой итерации цикла. Измерьте разницу в производительности и узнайте, если вам нужно.) – Tomalak

1

Альтернативное решение это принять ваш обратный вызов и в буквальном смысле сделать его имени функции.

Зачем мне это нужно?
Если функция выполняет что-то, где переменная должна обладать новой областью, то, скорее всего, анонимная функция требует выхода в новую функцию. Это также гарантирует, что дополнительная сложность не будет введена в ваш код, если вам придется копировать переменные или завершать обратные вызовы. Вы код остаетесь простым и самоописательным.

Пример:

function getGoogleAndAlertIfSuccess(attemptNumber) { 
    $.get('http://www.google.com/', function() { 
     alert(attemptNumber); 
    }); 
} 

function testGoogle() { 
    for (var i=0; i<2; i++) { 
     getGoogleAndAlertIfSuccess(i); 
    } 
} 
Смежные вопросы