I want to understand how and when javascript engines pass values to callback functions, I have tried debugging and looking up online but I am unable to find exact answer, consider the following example:
for(var i = 0; i < 4; i++) {
console.log("outside callback " + i + " ");
setTimeout(function test(){
console.log("inside callback " + i + " ");
},100);
}
this prints following output
outside callback 0
outside callback 1
outside callback 2
outside callback 3
inside callback 4
inside callback 4
inside callback 4
inside callback 4
If I just change the declaration of i variable using let keyword as follows:
for(let i = 0; i < 4; i++) {
console.log("outside callback " + i + " ");
setTimeout(function test(){
console.log("inside callback " + i + " ");
},100);
}
It results in the following output:
outside callback 0
outside callback 1
outside callback 2
outside callback 3
inside callback 0
inside callback 1
inside callback 2
inside callback 3
When debugging this in Chrome, it shows i in first example as closure scope for test function and block scope in second example.
This relates to a frequently asked question about JavaScript closure inside loops – simple practical example , which goes into detail about why inside call back functions created in a loop, variables declared using var
have the values they reached when the loop ended: while they may have held different values when the callback was setup, when the call back is performed, asynchronously, some time later, the loop has long finished and left variables inside a closure with the values they held when the loop completed.
From ECMAScript version 6 onwards, variables declared using let
are block scoped and cannot be accessed outside the code block in which they are defined. In addition variables defined in a for
loop control statement receive special treatment.
for
loop body, not the block of code containing the for
loop.A new binding for let
variables is created for each iteration of the loop. This means each iteration can have its own set of variable values, held in a lexical environment record created for the iteration, that can be used in a closure and accessed by callback functions defined within the loop.
The value of loop counters in each lexical environment record is designed to make the use of multiple environment records largely transparent:
for( part1; part2; part3) {
// loop body code
}
let
defined variables within the for
statement have the values they would have after executing part1
and part2
.part3
and part2
in the for
statement above.for
loop body access the value of let
defined variables held at the end of the iteration in which the call back was set up, excluding side effects of evaluating part3
of the for
statement.