Skip to content
Theory · Closures and this · lesson 4 of 11

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:

JavaScript
let n = 1;
const show = () => n;

n = 2;
console.log(show()); // 2

Let's go through it line by line:

  1. let n = 1 creates a variable with the value 1.
  2. const show = () => n creates a function that reads n. It does not copy the value 1 for itself.
  3. n = 2 changes that very variable.
  4. show() reads n at the moment of the call and returns 2.

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:

JavaScript
const fns = [];
for (let i = 0; i < 3; i++) {
    fns.push(() => i * 10);
}

console.log(fns[0](), fns[1](), fns[2]()); // 0 10 20

A variable declared with let in the for header is created anew on each iteration of the loop. Each lap goes like this:

  1. A new i is created and gets the value of the previous one.
  2. From the second lap on, i++ is applied to the new i first, and then the condition i < 3 checks it.
  3. 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:

  1. The last iteration works with i equal to 2.
  2. i++ increases i to 3.
  3. The check i < 3 gives false and 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:

JavaScript
const fns = [];
for (var i = 0; i < 3; i++) { // ✗
    fns.push(() => i * 10);
}

console.log(fns[0](), fns[1](), fns[2]()); // 30 30 30

There 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.
  • let in the for header creates a new variable on each iteration, so each function from the loop has its own i.
  • var gives 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.

Sign in another way