繁体   English   中英

了解 Javascript/Node 中闭包的变量捕获

[英]Understanding variable capture by closures in Javascript/Node

除了标准之外,Javascript 中是否有明确的变量捕获来源(阅读标准很痛苦)?

在下面的代码中, i是按值复制的:

for (var i = 0; i < 10; i++)
{
    (function (i)
    {
        process.nextTick(function ()
        {
            console.log(i)
        })
    }) (i)
}

所以它打印 1..10。 process.nextTick是节点中setTimeout(f,0)的模拟。

但在接下来的代码中,我似乎没有被复制:

for (var i = 0; i < 10; i++)
{
        var j = i
        process.nextTick(function ()
        {
            console.log(j)
        })
}

它打印 9 10 次。 为什么? 与解释这个具体的捕获案例相比,我对参考/一般文章更感兴趣。

我没有方便的参考。 但底线是:首先,您明确将i传递给匿名 function,这会创建一个新的 scope。您不会在第二个中为ij创建新的 scope。 此外,JavaScript 始终捕获变量,而不是值。 所以你也可以修改 i 。

JavaScript var关键字有 function scope,而不是块 scope。因此 for 循环不会创建 scope。

请注意,非标准let关键字具有本地 scope。

在 JavaScript 中,函数在 scope 中定义的变量包含在它们自己之外的变量中,这样它们就有一个对变量的“活”引用,而不是它在任何特定时间的值的快照。

因此,在第二个示例中,您创建了十个匿名函数(在process.nextTick(function(){...})中),其中包含变量j (和i ,在创建匿名 function 时它们始终具有相同的值)。 这些函数中的每一个都在外部 for 循环完全运行一次使用j的值,因此在调用每个函数时j=i=10 也就是说,首先你的 for 循环完全运行,然后你的匿名函数运行并使用j的值,它已经设置为 10!

在你的第一个例子中,情况有点不同。 通过将对process.nextTick(...)的调用包装在它自己的匿名 function 中,并通过调用包装器 function 将i的值绑定到函数局部 scope 中(顺便将旧变量i隐藏到 function 参数i中) ,您在那一刻捕获变量i的值,而不是保留对i封闭引用,其值在内部匿名函数的封闭中发生变化。

为了稍微阐明您的第一个示例,请尝试更改匿名包装器 function 以使用名为x ( (function (x) { process.nextTick(...); })(i) ) 的参数。 在这里,我们清楚地看到x在匿名 function 被调用时采用i中的值,因此它将获得 for 循环 (1..10) 中的每个值。

它在您的第二个示例中被复制(或分配),只是变量j只有一个副本,并且它将具有它最后一次拥有的值,即 9(您的 for 循环的最后一个版本)。 您需要一个新的 function 闭包来为for循环的每个 rev 创建变量的新副本。 你的第二个例子只有一个变量,它对你的for循环的所有 revs 都是通用的,因此它只能有一个值。

我不知道关于这个主题的任何权威文章。

javascript 中的变量作用域为 function 级别。 javascript 中没有块作用域。因此,如果您希望 for 循环的每个 rev 都有一个新版本的变量,则必须使用新的 function(创建一个 function 闭包)来每次通过循环for 如果没有 function 闭包,一个变量将只有一个值,该值将对该变量的所有用户共享。

当你声明一个变量时,比如你的var j = i; 在 function 开头以外的某个位置,javascript 将定义提升到 function 的顶部,您的代码将等同于此:

var j;
for (var i = 0; i < 10; i++)
{
        j = i;
        process.nextTick(function ()
        {
            console.log(j)
        })
}

这称为variable hoisting ,如果您想了解更多相关信息,可以谷歌这个术语。 但是,重点是只有 function scope,所以在 function 中任何地方声明的变量实际上是在 function 的顶部声明一次,然后分配给 function 中的任何地方。

Mozilla开发者网络有一个非常好的写作:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

对于第一个示例, i不相同。

for (var i = 0; i < 10; i++) {
    (function (i) { // 📸 capture i as the new i in this closure scope.
        process.nextTick(function () {
            console.log(i) // 🏞 reference new i (captured).
        })
    })(i)
}

闭包/价值捕获 - Rosetta 代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM