我是一位经验丰富的软件开发人员,但对于JS和节点来说却是一个新手。我不是超级嵌套代码的忠实粉丝,所以我一直试图将回调分解为自己的函数。虽然在回调触发时弄清楚如何保持范围,但我遇到了麻烦。四处搜索我读到如果我在回调上创建了一个闭包它可以工作,但它似乎没有按照我预期的方式工作。
这是一个非常简单的代码,对我来说不起作用:
function writeBody()
{
res.end("<h1> Hooray! </h1>");
}
http.createServer(function(req, res)
{
res.writeHead('Content-Type', 'text/html');
setTimeout(function(){writeBody()}, 2000);
}).listen(8000);
我认为通过在function()闭包中包装writeBody()调用,我会在超时后获得我需要的作用域,但是当writeBody()触发时我得到
ReferenceError:res未定义
谁能告诉我,我做错了什么?
答案 0 :(得分:36)
基本上,不是闭包如何工作,函数继承它们的外部作用域就是它的工作方式。
// this function only inherits the global scope
function writeBody()
{
res.end("<h1> Hooray! </h1>");
}
http.createServer(function(req, res) // a new local varaible res is created here for each callback
{
res.writeHead('Content-Type', 'text/html');
// annonymous function inheris both the global scope
// as well as the scope of the server callback
setTimeout(function(){
// the local variable res is available here too
writeBody()
}, 2000);
}).listen(8000);
要使其正常工作,只需将res
对象传递给函数,因为它在超时回调中可用。
function writeBody(res)
{
// NOT the same variable res, but it holds the same value
res.end("<h1> Hooray! </h1>");
}
http.createServer(function(req, res)
{
res.writeHead('Content-Type', 'text/html');
setTimeout(function(){
writeBody(res); // just pass res
}, 2000);
}).listen(8000);
但是你需要注意这样的事情:
for(var i = 0; i < 10; i++) { // only one i gets created here!()
setTimeout(function() {
console.log(i); // this always references the same variable i
}, 1000);
}
这将打印10
十次,因为引用相同,i
一直递增到10
。如果你想拥有为每个变量创建一个新变量所需的不同数字,可以将setTimeout
包装到一个匿名自我函数中,然后通过i
作为参数传递,或者通过调用设置timouet并接收i
作为参数的其他方法。
// anoynmous function version
for(var i = 0; i < 10; i++) {
(function(e){ // creates a new variable e for each call
setTimeout(function() {
console.log(e);
}, 1000);
})(i); // pass in the value of i
}
// function call version
for(var i = 0; i < 10; i++) {
createTimeoutFunction(i);
}
答案 1 :(得分:2)
您也可以嵌套函数,因此它们共享范围,即
http.createServer(function(req, res)
{
function writeBody()
{
res.end("<h1> Hooray! </h1>");
}
res.writeHead('Content-Type', 'text/html');
setTimeout(function(){writeBody()}, 2000);
}).listen(8000);
我经常发现这比总是传递一堆变量更容易保留在范围内,尽管这意味着你不能在其他地方重用该函数。
答案 2 :(得分:0)
您可以在回调中传递回复:
http.createServer(function(req, res)
{
res.writeHead('Content-Type', 'text/html');
setTimeout(function(){writeBody(res)}, 2000);
}).listen(8000);
答案 3 :(得分:0)
我其实喜欢贾斯汀科马克的回答。这是我最近编码的一个更极端的例子。
var Func4 = function(req, res)
{
var collectionName = "parts";
var f0 = function() {mongodbClient.collection(collectionName, f1);};
var f1 = function(err, coll) {coll.ensureIndex("item", f2);};
var f2 = function(err, indexname)
{
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("index name = " + indexname);
res.end();
};
f0();
};
大多数人会告诉我(他们这样做)这是编写该代码的正确方法。
var Func4 = function(req, res)
{
var collectionName = "parts";
mongodbClient.collection(collectionName, function(err, coll) {
coll.ensureIndex("item", function(err, indexname) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("index name = " + indexname);
res.end();
})});
};
也许我是一个n00b,但我发现嵌套回调有点难以理解。我也承认一堆f0,f1,f2功能很蹩脚。无论哪种方式,这都是范围的一个很好的例子。