JavaScript 有一个特点, 也许会让开发者头痛, 是与循环和作用域相关的.
举个例子:
- const operations = []
- for (var i = 0; i <5; i++) {
- operations.push(() => {
- console.log(i)
- })
- }
- for (const operation of operations) {
- operation()
- }
它基本是循环了 5 次, 将一个函数添加到 operations 数组里面. 这个函数可打印出循环变量索引值 i.
运行这些函数后
期望的结果应该是:
0 1 2 3 4
但实际发生的是这样的:
5 5 5 5 5
为什么会有这种情况 因为使用的是 var.
由于提升了 var 变量, 上面的代码等同于
- var i;
- const operations = []
- for (i = 0; i <5; i++) {
- operations.push(() => {
- console.log(i)
- })
- }
- for (const operation of operations) {
- operation()
- }
因此, 在 forof 循环中, i 依然是可见的, 它等于 5, 并且每次在函数中涉及到 i , 都将使用这个值.
那么我们应该如何做让其变成我们想的这样呢
最简单的方案是用 let 声明. 在 ES2015 中介绍到, 它们有很大的帮助, 能避免关于使用 var 声明的一些奇怪问题.
简单的在循环变量时将 var 变成 let , 能够很好的运行:
- const operations = []
- for (let i = 0; i <5; i++) {
- operations.push(() => {
- console.log(i)
- })
- }
- for (const operation of operations) {
- operation()
- }
这是输出结果:
0 1 2 3 4
这是怎么实现的呢 这是因为每次循环重复的时候, 都将重新创造 i , 同时每个函数添加 operations 数组时, 能获取它本身的 i.
记住你不能使用 const 在这种情况下, 因为这会导致 for 在第二次循环时, 尝试赋新值报错.
另外一个非常普遍的解决这个问题是使用 preES6 代码, 同时它被称作即时调用函数表达式 (IIFE).
在这种情况下, 你可以包装整个函数, 并将 i 绑定在它上面. 自这种方式, 你正在创造一个能立即执行的函数, 你从其返回的一个新的函数. 因此我们可以稍后执行它.
- const operations = []
- for (var i = 0; i <5; i++) {
- operations.push(((j) => {
- return () => console.log(j)
- })(i))
- }
- for (const operation of operations) {
- operation()
- }
来源: https://www.cnblogs.com/jtjds/p/9619492.html