Các vòng lặp và phạm vi JavaScript

Có một tính năng của JavaScript có thể gây ra một số đau đầu cho các nhà phát triển, liên quan đến vòng lặp và phạm vi. Tìm hiểu một số thủ thuật về vòng lặp và phạm vi với var và let

Có một tính năng củaJavaScriptđiều đó có thể gây ra một số đau đầu cho các nhà phát triển, liên quan đến các vòng lặp và phạm vi.

Lấy ví dụ sau:

const operations = []

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

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

Về cơ bản, nó lặp đi lặp lại và trong 5 lần nó thêm một hàm vào một mảng được gọi là các phép toán. Bảng điều khiển chức năng này ghi lại biến chỉ số vòng lặpi.

Sau đó nó chạy các chức năng này.

Kết quả mong đợi ở đây sẽ là:

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.

Since var declarations are hoisted, the above code equals to

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.

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.

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

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.

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.

Another way to solve this problem was very common in pre-ES6 code, and it is called Immediately Invoked Function Expression (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:

const operations = []

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

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


More js tutorials: