To my understanding, if the loop variable of a for loop is defined with var, then any change on that variable is applied globally. for example:
var printNumTwo;
for (var i = 0; i < 3; i ) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo());
In the above code, 3 will be printed into the console. Because in the last iteration the variable i will equal to 3. Therefore when printNumTwo is called, the update i will be returned. However if I use let this is not the case and the following behavior happens:
let printNumTwo;
for (let i = 0; i < 3; i ) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo());
The above code will print 2.
let printNumTwo;
for (let i = 0; i < 3; i ) {
if (i === 2) {
printNumTwo = function() {
return i;
};
i = 3;
}
}
console.log(printNumTwo());
however, the above code prints 3. What is the reason for this?
source: The first two code blocks are from here.
CodePudding user response:
In the first example, var
scopes i
to the function, so at the end of the loop i
changes i
from 2
to 3
and the loop stops. The only i
is now 3
and printNumTwo
returns i
which is 3
.
In the second example let
scopes i
to the block, so the function assigned to printNumTwo
closes over it. The last time the body of the loop runs, i
is 2
and printNumTwo
returns i
which is 2
. (A new i
is created with the value 3
but no function is created that uses it).
In the third example, let
scopes i
to the block and the same thing happens except you have i = 3;
which changes every i
to 3
. So the last function that is created (as well as the previous ones which are overwritten) returns i
which is 3
.
CodePudding user response:
however, the above code prints 3. What is the reason for this?
Because you assign 3
to the i
variable that printNumTwo
closes over. It doesn't matter that the assignment happens after printNumTwo
is created, only that it is the variable that printNumTwo
is using.
The difference between var
and let
in for
loops is that a new variable is created for the body of the loop on each loop iteration with let
. But you're assigning to that variable within the loop body, so the function closing over (printNumTwo
) it sees that value later when you call it.
It's exactly like this:
function create(i) {
// This function closes over `i`
const fn = function() {
return i;
};
// This modifies the `i` it closes over
i;
return fn;
}
const printValue1 = create(1);
console.log(printValue1()); // 2 (1 1)
const printValue27 = create(27);
console.log(printValue27()); // 28 (27 1)
In response to a comment on the question, you've said:
same thing also happens in the second code block (i in the update section in for loop ) but the answer is 2
Ah! Now I see why there's a misunderstanding. You're right that there's an update to i
— but it's not the i
that printNumTwo
closes over.
The update section of the for
is performed on the new variable for the next iteration at the start of the next iteration, not on the one for the iteration that just finished at the end of the iteration. It does this:
- Create a new iteration variable (let's call it
iNew
) - Assign the value of the old iteration variable (let's call it
iOld
) to the new variable (iNew = iOld
) - Run the update code using
iNew
(iNew
)
That's why i
(the old one) remains 2
; it's the new one that becomes 3
.
CodePudding user response:
Scope of var is the function and scope of let is the block.
So when assigning value to the printNumTwo
function, the value of i
is still 2. doesn't matter what happens to i
later.
The behavior of var and let you can understand from this example.
for(var i=0; i<3; i ){
setTimeout(() => console.log('var', i))
}
for(let i=0; i < 3; i ){
setTimeout(() => console.log('let',i))
}