【翻译】JavaScript循环和作用域
我的翻译小站:https://www.zcfy.cc/article/javascript-loops-and-scope
翻译原文链接:https://flaviocopes.com/javascript-loops-and-scope/JavaScript有一个特点,也许会让开发者头痛, 是与循环和作用域相关的.
举个例子:
1 2 3 4 5 6 7 8 9 10 11 | const operations = [] for ( var i = 0; i < 5; i++) { operations.push(() => { console.log(i) }) } for (const operation of operations) { operation() } |
它基本是循环了5次,将一个函数添加到operations数组里面。这个函数可打印出循环变量索引值i
.
运行这些函数后
期望的结果应该是:
1 2 3 4 5 | 0 1 2 3 4 |
但实际发生的是这样的:
1 2 3 4 5 | 5 5 5 5 5 |
为什么会有这种情况? 因为使用的是var
.
由于提升了var
变量, 上面的代码等同于
1 2 3 4 5 6 7 8 9 10 11 12 | var i; const operations = [] for (i = 0; i < 5; i++) { operations.push(() => { console.log(i) }) } for (const operation of operations) { operation() } |
因此,在for-of循环中, i
依然是可见的, 它等于5,并且每次在函数中涉及到i
,都将使用这个值。
那么我们应该如何做让其变成我们想的这样呢?
最简单的方案是用 let
声明. 在ES2015中介绍到, 它们有很大的帮助,能避免关于使用var
声明的一些奇怪问题。
简单的在循环变量时将var
变成 let
,能够很好的运行:
1 2 3 4 5 6 7 8 9 10 11 | const operations = [] for ( let i = 0; i < 5; i++) { operations.push(() => { console.log(i) }) } for (const operation of operations) { operation() } |
这是输出结果:
1 2 3 4 5 | 0 1 2 3 4 |
这是怎么实现的呢?这是因为每次循环重复的时候,都将重新创造i
,同时每个函数添加operations
数组时,能获取它本身的i
。
记住你不能使用 const
在这种情况下, 因为这会导致for
在第二次循环时, 尝试赋新值报错。
另外一个非常普遍的解决这个问题是使用pre-ES6代码, 同时它被称作即时调用函数表达式 (IIFE).
在这种情况下,你可以包装整个函数,并将i
绑定在它上面。自这种方式,你正在创造一个能立即执行的函数,你从其返回的一个新的函数。因此我们可以稍后执行它。
1 2 3 4 5 6 7 8 9 10 11 | const operations = [] for ( var i = 0; i < 5; i++) { operations.push(((j) => { return () => console.log(j) })(i)) } for (const operation of operations) { operation() } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?