This helped me understand var more (var vs let in the context of closure) :
Example 1: Using let
function makeFunctionArray() {
const arr = [];
for (let i = 0; i < 5; i++) {
arr.push(function () {
console.log(i);
});
}
return arr;
}
const functionArr = makeFunctionArray();
functionArr[0]();
Iterations:
Iteration 1 (i = 0):
arr.push(function () { console.log(i); }); (pushes a function that
logs i when called, where i is captured as 0 during this iteration).
Iteration 2 (i = 1):
arr.push(function () { console.log(i); }); (pushes a function that
logs i when called, where i is captured as 1 during this iteration).
Iteration 3 (i = 2):
arr.push(function () { console.log(i); }); (pushes a function that
logs i when called, where i is captured as 2 during this iteration).
Iteration 4 (i = 3):
arr.push(function () { console.log(i); }); (pushes a function that
logs i when called, where i is captured as 3 during this iteration).
Iteration 5 (i = 4):
arr.push(function () { console.log(i); }); (pushes a function that
logs i when called, where i is captured as 4 during this iteration).
Return and Execution:
const functionArr = makeFunctionArray(); (function array is created
and assigned to functionArr). functionArr0; (calls the first
function in the array, which logs the captured value of i during its
iteration, i.e., 0).
Example 2: Using var
unction makeFunctionArray() {
const arr = [];
for (var i = 0; i < 5; i++) {
arr.push(function () {
console.log(i);
});
}
return arr;
}
const functionArr = makeFunctionArray();
functionArr[0]();
Iterations:
Iteration 1 (i = 0):
arr.push(function () { console.log(i); }); (pushes a function into the array that references the shared variable i).
Iteration 2 (i = 1):
arr.push(function () { console.log(i); }); (pushes another function into the array that still references the shared variable i).
- Iteration 3 (i = 2):
arr.push(function () { console.log(i); }); (similarly, pushes another function into the array that still references the shared variable i).
- Iteration 4 (i = 3):
arr.push(function () { console.log(i); }); (continues the pattern, pushing a function that references the shared variable i).
- Iteration 5 (i = 4):
arr.push(function () { console.log(i); }); (pushes the final function into the array, all functions still referencing the shared variable i).
Return and Execution:
Return and Execution: const functionArr = makeFunctionArray(); (function array is created and assigned to functionArr).
functionArr0; (calls the first function in the array, which
references the shared variable i, whose final value after the loop is
5). Output: 5
My stupid explanation : var i is not evaluated and left until after the calling of the function in the array, it is referenced as i and not assigned a value
in the loop(unlike the const it is assigned a value then and there),
the value var is assigned is the latest change we make to i, which is
finishing the for loop at 4 and then it is dead by the end of the
function = 5; it exits the loop at 5; so basically var is the value at
which it dies. that's Javascript behavior right there don't blame me.
Practically :
if you have a function that references a var variable declared outside of it, you should consider the latest assignment or
change to that variable to determine its value when the function is
called. This is particularly important in scenarios like loops where
the variable may be modified multiple times before the function is
invoked.
Explanation : With var, there is only one variable i that is shared
among all functions pushed into the array. All the functions inside
the array form closures that reference the same i. When any function
in the array is called (e.g., functionArr0) after the loop has
completed, it will print the current value of the shared variable i,
which is 5. This behavior occurs because var has function scope rather
than block scope, and it does not create a new i for each iteration of
the loop. Instead, there's a single i that gets updated in each
iteration, and all functions in the array reference the same i.
xas an argument.