Как структурировать обратный вызов JavaScript так, чтобы функциональный объем сохранялся правильно

Я использую XMLHttpRequest, и я хочу получить доступ к локальной переменной в функции обратного вызова успеха.

Вот код:

function getFileContents(filePath, callbackFn) {  
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            callbackFn(xhr.responseText);
        }
    }
    xhr.open("GET", chrome.extension.getURL(filePath), true);
    xhr.send();
}

И я хочу назвать его как это:

var test = "lol";

getFileContents("hello.js", function(data) {
    alert(test);
});

Здесь, test был бы вне объема функции обратного вызова, так как только переменные функции включения доступны в функции обратного вызова. Что состоит в том, чтобы передать лучший способ test к функции обратного вызова так alert(test); отобразится test правильно?

Править:

Теперь, если у меня есть следующий код, называя функцию, определяемую выше:

for (var test in testers) {
    getFileContents("hello.js", function(data) {
        alert(test);
    });
}

alert(test); кодируйте только печатает последнее значение test от for цикл. Как я делаю его так, чтобы это распечатало значение test в течение времени, в который функция getFileContents был назван? (Я хотел бы сделать это без изменения getFileContents потому что это - очень общая функция помощника, и я не хочу делать это конкретным путем передачи определенной переменной как test к нему.

33
задан Chetan 24 May 2010 в 23:21
поделиться

3 ответа

С предоставленным вами кодом тест по-прежнему будет в области действия обратного вызова. xhr не будет, кроме xhr.responseText , переданного как данные .

Обновлено из комментария :

Предполагая, что ваш код выглядит примерно так:

for (var test in testers)
  getFileContents("hello"+test+".js", function(data) {
    alert(test);
  });
}

При запуске этого скрипта test будет присвоено значение ключей в тестерах - getFileContents вызывается каждый раз, что запускает запрос в фоновом режиме. По завершении запроса он вызывает обратный вызов. тест будет содержать ОКОНЧАТЕЛЬНОЕ ЗНАЧЕНИЕ из цикла, поскольку этот цикл уже завершил выполнение.

Есть метод, который вы можете использовать, называемый закрытием, который решит такую ​​проблему. Вы можете создать функцию, которая возвращает вашу функцию обратного вызова, создав новую область видимости, которую вы можете удерживать для своих переменных с помощью:

for (var test in testers) {
  getFileContents("hello"+test+".js", 
    (function(test) { // lets create a function who has a single argument "test"
      // inside this function test will refer to the functions argument
      return function(data) {
        // test still refers to the closure functions argument
        alert(test);
      };
    })(test) // immediately call the closure with the current value of test
  );
}

Это в основном создаст новую область видимости (вместе с нашей новой функцией), которая будет «удерживать» значение тест .

Другой способ записать то же самое:

for (var test in testers) {
  (function(test) { // lets create a function who has a single argument "test"
    // inside this function test will refer to the functions argument
    // not the var test from the loop above
    getFileContents("hello"+test+".js", function(data) {
        // test still refers to the closure functions argument
        alert(test);
    });
  })(test); // immediately call the closure with the value of `test` from `testers`
}
41
ответ дан 27 November 2019 в 18:26
поделиться

В этом сценарии test будет разрешен, как вы и ожидали, но значение this может быть другим. Обычно, чтобы сохранить область видимости, вы делаете его параметром асинхронной функции, как например:

function getFileContents(filePath, callbackFn, scope) {  
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            callbackFn.call(scope, xhr.responseText);
        }
    }
    xhr.open("GET", chrome.extension.getURL(filePath), true);
    xhr.send();
}


//then to call it:
var test = "lol";

getFileContents("hello.js", function(data) {
    alert(test);
}, this);
2
ответ дан 27 November 2019 в 18:26
поделиться

JavaScript использует лексическую область видимости , что в основном означает, что ваш второй пример кода будет работать так, как вы предполагаете.

Рассмотрим следующий пример, заимствованный из Definitive Guide Дэвида Фланагана 1 :

var x = "global";

function f() {
  var x = "local";
  function g() { alert(x); }
  g();
}

f();  // Calling this function displays "local"

Также имейте в виду, что в отличие от C, C ++ и Java, JavaScript не имеет области видимости на уровне блоков.

Кроме того, вам может быть интересно ознакомиться со следующей статьей, которую я настоятельно рекомендую:


1 Дэвид Фланаган: JavaScript - Окончательный Руководство , четвертое издание, стр. 48.

7
ответ дан 27 November 2019 в 18:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: