JavaScript循环和范围

There is one feature of JavaScript that might cause a few headaches to developers, related to loops and scoping.

JavaScript的一项功能可能会引起开发人员的麻烦,与循环和作用域有关。

Take this example:

举个例子:

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

It basically iterates and for 5 times it adds a function to an array called operations. This function console logs the loop index variable i.

它基本上进行迭代,并向数组中添加一个函数5次,称为操作。 该功能控制台记录循环索引变量i

Later it runs these functions.

稍后它将运行这些功能。

The expected result here should be:

这里的预期结果应该是:

0
1
2
3
4

but actually what happens is this:

但是实际上发生了什么:

5
5
5
5
5

Why is this the case? Because of the use of var.

为什么会这样呢? 由于使用var

Since var declarations are hoisted, the above code equals to

由于var声明已吊起 ,因此上述代码等于

var i;
const operations = []

for (i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

so, in the for-of loop, i is still visible, it’s equal to 5 and every reference to i in the function is going to use this value.

因此,在for-of循环中, i仍然可见,它等于5,并且函数中对i每个引用都将使用该值。

So how should we do to make things work as we want?

那么,我们应该如何做才能使事情如我们所愿?

The simplest solution is to use let declarations. Introduced in ES6, they are a great help in avoiding some of the weird things about var declarations.

最简单的解决方案是使用let声明。 它们在ES6中引入,对于避免有关var声明的某些怪异现象提供了很大的帮助。

Changing var to let in the loop variable is going to work fine:

更改varlet循环变量可以正常工作:

const operations = []

for (let i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

Here’s the output:

这是输出:

0
1
2
3
4

How is this possible? This works because on every loop iteration i is created as a new variable each time, and every function added to the operations array gets its own copy of i.

这怎么可能? 之所以operations ,是因为在每次循环迭代中,每次都会将i创建为一个新变量,并且添加到operations数组的每个函数都将获得自己的i副本。

Keep in mind you cannot use const in this case, because there would be an error as for tries to assign a new value in the second iteration.

请记住,您不能使用const在这种情况下,因为会有一个错误for尝试分配在第二次迭代的新值。

Another way to solve this problem was very common in pre-ES6 code, and it is called Immediately Invoked Function Expression (IIFE).

解决此问题的另一种方法在ES6之前的代码中非常普遍,称为立即调用函数表达式 (IIFE)。

In this case you can wrap the entire function and bind i to it. Since in this way you’re creating a function that immediately executes, you return a new function from it, so we can execute it later:

在这种情况下,您可以包装整个函数并将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://flaviocopes.com/javascript-loops-and-scope/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值