如何构造javascript回调,以便正确维护函数范围

Che*_*tan 33 javascript scope function callback

我正在使用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();
}
Run Code Online (Sandbox Code Playgroud)

我想这样称呼它:

var test = "lol";

getFileContents("hello.js", function(data) {
    alert(test);
});
Run Code Online (Sandbox Code Playgroud)

这里test将超出回调函数的范围,因为只有封闭函数的变量可以在回调函数中访问.传递test给回调函数的最佳方法是什么,以便正确alert(test);显示test

编辑:

现在,如果我有以下代码调用上面定义的函数:

for (var test in testers) {
    getFileContents("hello.js", function(data) {
        alert(test);
    });
}
Run Code Online (Sandbox Code Playgroud)

alert(test);代码只打印的最后一个值testfor环.如何使它打印出调用test函数时的值getFileContents?(我想这样做而不改变,getFileContents因为它是一个非常通用的辅助函数,我不想通过传递一个特定的变量test来使它具体化.

gna*_*arf 41

您提供的代码test仍将在回调范围内. xhr不会,比其他xhr.responseText被传递进来data.

从评论更新:

假设你的代码看起来像这样:

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

当此脚本运行时,test将分配键的值testers- getFileContents每次调用,在后台启动请求.当请求完成时,它会调用回调. 将从循环中test包含FINAL VALUE,因为该循环已经完成执行.

你可以使用一种称为闭包的技术来解决这类问题.您可以创建一个返回回调函数的函数,创建一个可以保存到变量的新范围:

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
  );
}
Run Code Online (Sandbox Code Playgroud)

这基本上会创建一个新的范围(以及我们的新功能),它将"保持"其价值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`
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*llo 7

JavaScript使用词法作用域,这基本上意味着您的第二个代码示例将像您打算如何工作一样工作.

考虑以下示例,借用David Flanagan的权威指南1:

var x = "global";

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

f();  // Calling this function displays "local"
Run Code Online (Sandbox Code Playgroud)

还要记住,与C,C++和Java不同,JavaScript没有块级范围.

此外,您可能还有兴趣查看以下文章,我强烈推荐:


1 David Flanagan:JavaScript - The Definitive Guide,Fourth Edition,Page 48.