Closures in a loop
In the previous lesson every function card had a note clipped to it — a variable. The note is not a copy: the function holds the variable itself. So the function sees the value that is in the variable at the moment the function is called:
let n = 1;
const show = () => n;
n = 2;
console.log(show()); // 2Let's go through it line by line:
let n = 1creates a variable with the value1.const show = () => ncreates a function that readsn. It does not copy the value1for itself.n = 2changes that very variable.show()readsnat the moment of the call and returns2.
This matters when functions are created in a loop and called after it. Then everything comes down to one question: how many notes were there in the loop — one for all, or a separate one on each lap?
let in the for header
The loop below puts three arrow functions into an array. They are called after the loop, and each one returns its own number:
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i * 10);
}
console.log(fns[0](), fns[1](), fns[2]()); // 0 10 20A variable declared with let in the for header is created anew on each iteration of the loop. Each lap goes like this:
- A new
iis created and gets the value of the previous one. - From the second lap on,
i++is applied to the newifirst, and then the conditioni < 3checks it. - The arrow function created on this lap closes over this
i.
Nothing changes that i afterwards: the next lap works with its own variable. Each card takes its own note away. So the first function returns 0, the second 10, the third 20.
Here the analogy is not exact: a new note is not blank. The value from the previous lap's note is copied onto it right away.
A for…of loop with const works the same way: each iteration has its own variable.
One var for the loop
The keyword var creates a function-scoped variable and ignores blocks. Such an i is one for the whole loop, not a separate one per iteration. All the functions created in the loop close over the same variable: one note is clipped to all the cards at once.
They read its value when they are called, after the loop has ended. If a function is called right away, in the same iteration, let and var give the same result.
After the loop, i holds the value at which the condition became false. In the loop for (var i = 0; i < 3; i++) that is 3, not 2. The last steps of the loop go like this:
- The last iteration works with
iequal to2. i++increasesito3.- The check
i < 3givesfalseand stops the loop.
Interviewers like to ask this: functions are created in a loop with var, and you are asked what they return after the loop. The answer: they all return the same thing, the last value of i, while with let each one returns its own. A good answer also names the reason: var gives one variable for the whole loop, and the functions read it after the loop.
Common mistake
The loop with functions uses var instead of let:
const fns = [];
for (var i = 0; i < 3; i++) { // ✗
fns.push(() => i * 10);
}
console.log(fns[0](), fns[1](), fns[2]()); // 30 30 30There is no error, but all three functions return 30. They read one shared i, and at call time it already holds 3. Replace var with let: then each iteration gets its own i, and the program prints 0 10 20.
Key points
- A closure reads a variable's value at the moment of the call, not at the moment the function is created.
letin theforheader creates a new variable on each iteration, so each function from the loop has its owni.vargives one variable for the whole loop, so functions called after the loop see its last value.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.