27

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

在以下代码i中按值复制:

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

所以它打印 1..10。process.nextTicksetTimeout(f,0)in 节点的类比。

但在下一个代码中,我似乎没有被复制:

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

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

4

4 回答 4

18

我没有方便的参考。但底线是:首先,您显式地传入i一个匿名函数,该函数创建了一个新范围。您没有为任何一个ij第二个创建新范围。此外,JavaScript 总是捕获变量,而不是值。所以你也可以修改 i 。

JavaScriptvar关键字具有函数作用域,而不是块作用域。因此 for 循环不会创建范围。

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

于 2012-04-10T19:50:23.990 回答
5

在 JavaScript 中,函数包含在其自身范围之外的范围内定义的变量,使得它们具有对变量的“活动”引用,而不是任何特定时间其值的快照。

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

在您的第一个示例中,情况略有不同。通过将调用包装process.nextTick(...)在它自己的匿名函数中,并通过调用包装函数(并顺便将旧变量隐藏到函数参数中)将 的值绑定i到函数局部范围内,您可以捕获当时的变量,而不是保留其值在内部匿名函数的外壳中更改的封闭引用。iiii

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

于 2012-04-10T20:01:53.730 回答
5

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

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

javascript 中的变量的作用域是函数级别。javascript 中没有块作用域。因此,如果您希望 for 循环的每个 rev 都有一个新版本的变量,您必须使用一个新函数(创建一个函数闭包)在每次for循环中捕获该新值。如果没有函数闭包,一个变量将只有一个值,该值对该变量的所有用户都是通用的。

当您在函数开头以外的某个位置声明诸如 your 之类的变量var j = i;时,javascript 会将定义提升到函数的顶部,并且您的代码将与此等效:

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

这就是所谓variable hoisting的术语,如果您想了解更多相关信息,您可以使用 Google 搜索。但是,关键是只有函数作用域,因此在函数中任何位置声明的变量实际上在函数顶部声明一次,然后分配给函数中的任何位置。

于 2012-04-10T19:50:34.523 回答
0

对于第一个例子,i's 是不一样的。

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 Code

于 2021-02-23T02:13:21.363 回答