我正在使用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
)传递给它来使其具体化。
答案 0 :(得分:41)
使用您提供的代码test
仍将在回调范围内。除了xhr
传递xhr.responseText
之外,data
不会{。}}。
更新自评论:
假设您的代码如下所示:
for (var test in testers)
getFileContents("hello"+test+".js", function(data) {
alert(test);
});
}
当此脚本运行时,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
);
}
这将基本上创建一个新的范围(以及我们的新功能),它将“保持”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`
}
答案 1 :(得分:7)
JavaScript使用lexical scoping,这基本上意味着您的第二个代码示例将像您打算如何工作一样工作。
考虑以下示例,借用David Flanagan的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 David Flanagan:JavaScript - The Definitive Guide,第四版,第48页。
答案 2 :(得分:2)
在这种情况下,测试将按照您的预期得到解决,但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);
答案 3 :(得分:0)
我遇到了类似的问题。我的代码看起来像这样:
for (var i=0; i<textFilesObj.length; i++)
{
var xmlHttp=new XMLHttpRequest();
var name = "compile/" + textFilesObj[i].fileName;
var content = textFilesObj[i].content;
xmlHttp.onreadystatechange=function()
{
if(xmlHttp.readyState==4)
{
var responseText = xmlHttp.responseText;
Debug(responseText);
}
}
xmlHttp.open("POST","save1.php",true);
xmlHttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xmlHttp.send("filename="+name+"&text="+encodeURIComponent(content));
}
输出通常是最后一个对象的响应文本(但并非总是如此)。实际上它是随机的,甚至响应的数量也不同(对于恒定的输入)。事实证明该函数应该写成:
xmlHttp.onreadystatechange=function()
{
if(this.readyState==4)
{
var responseText = this.responseText;
Debug(responseText);
}
}
基本上,在第一个版本中,“xmlHttp”对象被设置为循环当前阶段的版本。大多数情况下,循环在任何AJAX请求完成之前就已经完成,因此在这种情况下,“xmlHttp”指的是循环的最后一个实例。如果该最终请求在其他请求之前完成,那么只要它们的就绪状态改变(即使它们的就绪状态是<4),它们都将打印来自最后一个请求的响应。
解决方法是将“xmlHttp”替换为“this”,这样每次调用回调时内部函数都会引用请求对象的正确实例。