Node.js 在本地范围内保留全局变量的副本

Node.js keeps a copy of a global variable inside local scope

我有一个 Node.js 应用程序,它使用请求库来获取多个 URL。代码是这样的:

for(var i = 0; i < docs.length; i++){

        var URL = docs[i].url;

        request(URL, function(error, response, html){
            console.log(URL);
            // Other code...
}

为了简单起见,假设 docs 包含 URL,例如 [URL1、URL2、URL3、.. .]。在第一次迭代中,URL = URL1 并且向 URL 发送了一个请求。在第二次迭代中,请求被发送到 URL2 等等。然而,在循环结束时,URL = URLn。在事件完成函数中,当我记录 URL 时,我总是得到 URLn。但是,我需要能够获得相应的 URLs 即 [URL1, URL2, URL3, ...].

知道吗,我如何维护 URL 的本地副本,当全局 URL 发生更改时该副本保持不变?

这一定很简单,但我想不出来

只需将代码包装在函数中或使用 forEach。发生这种情况是因为闭包范围。

docs.forEach(functiom(doc) {
    var URL = doc.url;

    request(URL, function(error, response, html){
         console.log(URL);
         // Other code...
    })
});

另一个修复

for(var i = 0; i < docs.length; i++){
    makeRequest(docs[i]);
}

function makeRequest(doc) {
    var URL = doc.url;

    request(URL, function(error, response, html){
        console.log(URL);
    });
}

还有一个更丑陋的修复,在 for 循环中有一个闭包

for(var i = 0; i < docs.length; i++){
    (function(doc) {
        var URL = doc.url;

        request(URL, function(error, response, html){
            console.log(URL);
            // Other code...
        });
    })(docs[i]);
}

如果你使用类似 JSHint 的东西,它会警告你不要在 for 循环中创建函数,因为它会导致这样的问题。

基本上,您在 JavaScript 中遇到的是正常行为,在 Node.js 中没有特殊行为。

JavaScript 中唯一定义作用域的是函数,函数可以访问它们自己的作用域以及任何 "outer" 作用域。

因此,解决方案是将您的代码包装在一个函数中,该函数将全局变量作为参数并将其作为参数提供给函数内的代码:每次调用该函数时都会对其进行评估,因此您的内部代码将获得自己的 "copy".

基本上你有两个选择。要么使用 立即执行的函数表达式 。这基本上只是一个无名(即匿名)函数,在定义的地方立即调用:

for(var i = 0; i < docs.length; i++){
  (function (url) {
    request(url, function(error, response, html){
      console.log(url); 
    });
  })(doc.url);
}

或者使用数组的内置 forEach 函数自动将其主体包装在函数中(产生相同的效果):

docs.forEach(function (url) {
  request(url, function(error, response, html){
    console.log(url); 
  });
});

您应该在 JavaScript here 中阅读闭包。

同时,简单来说,i 的值将在所有迭代结束时达到 n。因此,你每次都会得到 URLn。如果将请求包装在一个立即调用的函数表达式中,那么您将在作用域链中创建另一个级别。通过这样做,请求方法的回调将不会在全局范围内引用变量 i,而是在发送请求时在函数范围内可用的变量 i .以及您期望的 i 的值。

代码会是这样的:

for(var i = 0; i < docs.length; i++){
    var URL = docs[i].url;

    (function(currentURL) {
        //now the URL is preserved as currentURL inside the scope of the function
        request(currentURL, function(error, response, html){
            console.log(currentURL);
            // This value of currentURL is the one that was available in the scope chain
            // Other code...
        });
    })(URL);
}

只需使用 let 而不是 var,即:

for(let i = 0; i < docs.length; i++){

        let URL = docs[i].url;

        request(URL, function(error, response, html){
            console.log(URL);
            //other code...
}