通过Javascript / Node中的闭包了解variables捕获
除了标准之外,在JavaScript中是否有variables捕获的确切来源(阅读标准很痛苦)?
在下面的代码中, 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
传递给一个匿名函数,它创build一个新的范围。 你不是在第二次为i
或j
创build一个新的范围。 而且,JavaScript总是捕获variables,而不是值。 所以你也可以修改我。
JavaScript var
关键字具有function范围,而不是范围范围。 所以for循环不会创build一个范围。
请注意,非标准let
关键字具有本地范围。
在第二个例子中,它被复制(或分配),只是variablesj
只有一个副本,并且它将具有最后一个variablesj
的值(它是for循环的最后一个variables)。 您需要一个新的函数闭包来for
循环的每一次创build一个variables的新副本。 你的第二个例子只有一个variables,这个variables对于你的for
循环的所有转换是共同的,因此它只能有一个值。
我不知道有关这个话题的任何明确的写法。
javascript中的variables被限定在函数级别。 在JavaScript中没有块范围。 因此,如果你想为for循环的每一个版本添加一个variables的新版本,你必须使用一个新的函数(创build一个函数闭包)来每次通过for
循环捕获这个新的值。 如果没有函数闭包,那么这个variables只有一个值,这个variables的所有用户都是共同的。
当你声明一个variables,如你的var j = i;
在函数的开头以外的某个位置,JavaScript将定义提升到函数的顶部,并且您的代码变成等同于此:
var j; for (var i = 0; i < 10; i++) { j = i; process.nextTick(function () { console.log(j) }) }
这就是所谓的variable hoisting
,如果你想了解更多的信息,这是一个你可以谷歌的术语。 但是,重点是只有函数作用域,所以在函数的任何地方声明的variables实际上是在函数的顶部声明一次,然后分配给函数中的任何地方。
在JavaScript中,函数将variables定义在自己范围之外的variables中,以便对variables具有“活跃”引用,而不是在特定时间对其值的快照。
所以在你的第二个例子中,你创build了十个匿名函数(在process.nextTick(function(){...})
)中,它包含variablesj
(和i
,当匿名函数被创build时它们总是具有相同的值)。 每个函数在外部for循环完全运行之后使用j
的值,所以在调用每个函数时j=i=10
。 也就是说,首先你的for循环完全运行,然后你的匿名函数运行,并使用已经设置为10的j
的值!
在你的第一个例子中,情况有些不同。 通过在自己的匿名函数中将调用包装到process.nextTick(...)
中,并通过调用包装函数将函数本地作用域中的值绑定到函数参数i
(并附带地将旧variablesi
映射到函数参数i
) ,那么您捕获当时的variablesi
的值,而不是保留内部匿名函数附件中的值变化的i
的封闭引用 。
为了澄清你的第一个例子,试着改变匿名包装函数来使用一个名为x
( (function (x) { process.nextTick(...); })(i)
)的参数。 在这里,我们清楚地看到x
在当前调用匿名函数时的值,因此它将得到for循环中的每个值(1..10)。
Mozilla开发者networking有一个非常好的写法:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures