执行此代码时,我希望在控制台中看到10次写入100次(基于使用其他语言的经验):
const arr = [];
for (let i = 0; i < 10; i++)
arr.push(() => i * i);
arr.forEach(f => console.log(f()));
但是我得到每个数字的平方(从0到9)。这似乎与某些其他语言有所不同。
例如,使用 C#,您将获得100十次:
var list = new List<Func<int>>();
for(var i = 0; i < 10; i++)
list.Add(() => i * i);
list.ForEach(f => Console.WriteLine(f()));
Python 会打印81次十次:
arr = []
for i in range(10):
arr.append(lambda: i * i)
for f in arr:
print(f())
Java 不允许这样做,因为捕获的变量无法突变。
有关其他语言的示例,请参见this。
如下文Mark所述,当我们在JavaScript中使用 let (块作用域)时,i的值被捕获在每个匿名函数内的新作用域变量中。当您查看每个函数的实际定义时(使用: arr.forEach(f => console.dir(f)); ),您会看到一个范围(_loop)来自每次迭代都会捕获到我的价值:
[[Scopes]]: Scopes[3]
0: Closure (_loop) {i: 0}
[[Scopes]]: Scopes[3]
0: Closure (_loop) {i: 1}
and so on...
如果我们使用 var 重新运行此示例,毫不奇怪的是,缺少_loop块级作用域,并且我以相同的值(10)保存在模块/文件级作用域中每次迭代:
[[Scopes]]: Scopes[2]
0: Closure (./src/index.js) {i: 10}
答案 0 :(得分:3)
这是使用let
在javascript中声明变量的本质-它的作用域是for
循环块(在此暗示),这意味着每次迭代都会获得一个新的作用域名称,该名称在每个关闭。闭包并非全都使用相同的名称,就像在其他语言中使用闭包时一样,或者在使用var
声明变量时使用JS时也是如此。
要获得预期的行为,请使用var
:
const arr = [];
for (var i = 0; i < 10; i++)
arr.push(() => i * i);
arr.forEach(f => console.log(f()));
上面的代码是许多困惑的程序员的祸根,这是可以选择使用let
的原因之一。