All topics
Functionsintermediate

Closures Explained

How an inner function retains access to variables from its enclosing scope even after that outer function has finished executing.

A closure is a function that still has access to the variables of the scope it was created in, after that scope has finished.

A closure is like a backpack a function carries around: whatever variables were in scope when the function was created get zipped into that backpack, and the function can reach in and use them anywhere it goes, long after it's left the room (scope) where it was packed.

Key Concepts

1
    function counter() {
      let n = 0;
      return () => ++n;
    }
2
    const next = counter();
    next();   // 1
    next();   // 2
3
counter has returned. n should be gone, and it is not — the returned function keeps its scope alive.
countern
4
Why it works. A function's scope chain is fixed when it is defined. As long as something references the inner function, the outer scope it points at cannot be collected.
5
Each call creates a separate scope.
    const a = counter();
    const b = counter();
    a();  // 1
    a();  // 2
    b();  // 1   -- its own n
6
Which is what makes private state possible.
    function createAccount(initial) {
      let balance = initial;
      return {
        deposit: (x) => balance += x,
        getBalance: () => balance
      };
    }
7
    const acc = createAccount(100);
    acc.deposit(50);
    acc.getBalance();   // 150
    acc.balance;        // undefined -- unreachable
8
There is no way to read or change balance except through the methods. That is genuine privacy, and it was the only kind JavaScript had before class private fields.
balance
9
The loop trap, which is the standard question.
    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i));
    }
    // 3 3 3
10
All three closures captured the same i, and by the time they run the loop has finished. With let, each iteration gets a new binding, and the output is 0 1 2.
ilet0 1 2
11
Closures capture variables, not values. That single sentence explains the behaviour.
12
Where they leak memory. A closure holding a reference to something large keeps it alive:
13
    function setup() {
      const huge = new Array(1e6);
      return () => console.log('hi');   // does not use huge,
    }                                    // but may still retain it
14
Engines optimise this, and it is not guaranteed. An event handler that closes over a DOM node, and is never removed, keeps that node in memory after it leaves the page — which is the common real version of this.
15
Where you already use them. Every callback, every event handler, every useState setter in React, and every module built with an IIFE. Closures are not an advanced feature; they are how the language works.
useState
16
What the interviewer is probing.1. "What is a closure?" Probing: whether the definition is precise. Stalls: "A function inside a function." Moves up: a function together with the scope it was defined in, keeping those variables alive after the outer function returns.
17
2. "How do closures cause memory leaks?" Probing: the cost. Stalls: "They do not." *Moves up:* a retained closure keeps its entire scope reachable, so a large object captured but unused is never collected.
18
3. "Write a counter with a private count." Probing: the practical application. Stalls: "Use a global." Moves up: return functions from a factory that close over a local count, which nothing outside can reach.
19
4. "Why do all callbacks in a var loop see the final value?" Probing: the shared binding. Stalls: "They should see each value." Moves up: one binding is shared by every iteration; let creates one per iteration.