Declare variables in loop initializer statements

Read today to see the following sentences:

In Depth ES6 chapter 1, block-level scoped binding, described in the let declaration section of the loop on page 9

Each iteration of the loop creates a new variable and initializes it with the value of the variable of the same name from the previous iteration.

It is also mentioned in Chapter 3 of JavaScript Advanced Programming (4th edition), Language Basics, Section 3.3.2, page 28

When you declare iteration variables using let, the JavaScript engine declares a new iteration variable behind the scenes for each iteration loop.

Best practices

  1. forRecommended for use in loopsletTo declare the iteration variable
  2. for... inandfor... ofIt is recommended to useconstTo declare the iteration variable.

It’s kind of hard to see why we’re doing this, so let’s write some code to try it out.

Code practice

1. Asynchronous printing

for (var i = 0; i < 3; ++i) {
  setTimeout(() = > {console.log(i)})
}
Copy the code

Output 3, 3, 3

Reason: Var declarations promote variables. The function refers to I in the global scope, and by the time I is printed, the value of I has changed to 3.

Let’s do that

for (let i = 0; i < 3; ++i) {
  setTimeout(() = > {console.log(i)})
}
Copy the code

Output 0, 1, 2

Reason: Each iteration of the loop creates a new variable I in its block-level scope.

ES5

for (var i = 0; i < 3; ++i) {
  (function (i){
    setTimeout(() = > {console.log(i)})
  }(i))
}
Copy the code

Output 0, 1, 2

Cause: An artificial copy of I was made using closures.

2. Create functions in the loop

var fns = []
for (var i = 0; i < 3; ++i) {
  fns.push(() = > { console.log(i) })
}
fns.forEach(fn= > fn())
Copy the code

Output 3, 3, 3

Reason: Var declarations promote variables. The function uses the same I in the global scope. When printing I, the value of I has changed to 3.

Let’s do that

var fns = []
for (let i = 0; i < 3; ++i) {
  fns.push(() = > { console.log(i) })
}
fns.forEach(fn= > fn())
Copy the code

Output 0, 1, 2

Reason: Let creates a new variable I for each loop, and three functions refer to three different I.

ES5

var fns = []
for (var i = 0; i < 3; ++i) {
  (function (i) {
    fns.push(() = > { console.log(i) })
  }(i))
}
fns.forEach(fn= > fn())
Copy the code

Output 0, 1, 2

Reason: Using the nature of function scope, variable I is artificially copied in each loop.

Loop through using reference type variables

for (const i = {a: 0}; i.a < 3; i.a = i.a + 1) {
  i[`${i.a}`] = i.a
  setTimeout(() = > console.log(i), 1000 * i.a)
}
// { '0': 0, '1': 1, '2': 2, a: 3 }
// { '0': 0, '1': 1, '2': 2, a: 3 }
// { '0': 0, '1': 1, '2': 2, a: 3 }
Copy the code

Reason: The I copied by the JavaScript engine in each loop is actually an address to the heap memory object.

Since we are only changing the value of the heap object property, we are not changing the value of I (which is actually a pointer to the heap object), we can declare I using const, which gives the same result as we did with let and var.

If JavaScript provided an API to view the memory address of the variable stack, it would be easy to verify this statement.