【发布时间】:2019-10-11 10:40:09
【问题描述】:
当我执行此代码时,我预计会在控制台中看到 100 次写入 10 次(基于使用其他语言的经验):
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)来自每次迭代都会捕获 i 的值:
[[Scopes]]: Scopes[3]
0: Closure (_loop) {i: 0}
[[Scopes]]: Scopes[3]
0: Closure (_loop) {i: 1}
and so on...
如果我们使用 var 重新运行此示例,不出所料,_loop 块级范围会丢失,而 i 会保存在模块/文件级范围中,而不是使用相同的值 (10) for每次迭代:
[[Scopes]]: Scopes[2]
0: Closure (./src/index.js) {i: 10}
【问题讨论】:
-
'i' 不是参考,你怎么期望 100
-
@DennisVash 我知道这不是参考,但我的意思是它的行为与其他一些语言不同,例如尝试在 C# 中运行相同的代码。
-
@DennisVash 这不完全是关于原始值与参考值的问题(请参阅下面 Mark 的回复),因此请考虑删除或编辑您的 cmets 以确保其他人不会被误导。
-
您在 JS 中使用
let,在 C++ 中使用var,然后想知道为什么它们的结果不同? -
@NiettheDarkAbsol 我没有提供 C++ 中的示例,如果您指的是 C# 示例,它使用与 JavaScript var 非常不同的隐式类型局部变量。
标签: javascript closures