javascript - JavaScript 闭包行为异常
问题描述
当我执行此代码时,我预计会在控制台中看到 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不允许这样做,因为捕获的变量不能变异。
有关其他语言的示例,请参见此处。
正如 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):
[[Scopes]]: Scopes[2]
0: Closure (./src/index.js) {i: 10}
解决方案
let
这就是在 javascript 中使用声明变量的本质——它的作用域是(这里隐含的)for
循环块,这意味着每次迭代都会获得一个新的作用域名称,该名称在每个闭包中被捕获。闭包并不都使用相同的名称,就像它们在其他一些语言中一样,或者在 JS 中var
用于声明变量时。
要获得您的预期行为,请使用var
:
const arr = [];
for (var i = 0; i < 10; i++)
arr.push(() => i * i);
arr.forEach(f => console.log(f()));
上面的代码是许多困惑的程序员的祸根,这也是可以选择使用let
.
推荐阅读
- javascript - socket.io 连接的最佳实践
- javascript - 在 Heroku 上部署的 React Portfolio 不显示图像
- reactjs - React Hooks - 防止深度比较导致无限重新渲染
- twitter-bootstrap - 减少 bootstrap/primeng scss 导入足迹
- javascript - 如何更快地找出哪个 nodeunit 导致异步错误?
- javascript - Javascript在keyevent之后开始秒表
- python - 张量流。将图像从 PNG 转换为 JPEG
- typescript - 在标记的不相交联合上运行的通用函数 - 为什么这里需要类型规范?
- javascript - 如何使用对象文字中的方法访问 ID?
- javascript - 需要使用 Vanilla Javascript 隐藏/显示子菜单