Closures

A closure is a function that remembers the variables from the place it was created, even after that place is "gone."

What Is a Closure?

Every time a function is created inside another function, it keeps a link back to the variables that were around it at that moment. That link — the inner function plus the memory of its surroundings — is called a closure. This happens automatically; you don't need any special syntax to create one.

function makeCounter() {
  let count = 0;
  return function() {
    count = count + 1;
    return count;
  };
}

const counter = makeCounter();
counter(); // 1
counter(); // 2
counter(); // 3
  • makeCounter runs once, creates count, and returns an inner function
  • Normally, count would disappear once makeCounter finishes running
  • But the inner function closes over count, keeping it alive in memory
  • Each call to counter() reaches into that same preserved count and updates it
🎒 Real-life analogy

Imagine a backpack. When a function is created inside another function, it packs a backpack with copies of the variables it might need later, then carries that backpack wherever it goes — even long after the place it was packed in no longer exists. Every time you call the function, it reaches into its own backpack.

Why Do Closures Matter?

  • They let you create private variables — count above can't be accessed or changed directly from outside, only through the returned function.
  • They power many common patterns: counters, caches, event handlers that remember data, and the module pattern.
  • They're the mechanism behind most "state" in JavaScript before frameworks like React existed.

When Should We Use Them?

Any time you want a function to "remember" something between calls, without storing that thing in a global variable everyone can accidentally change, reach for a closure.

A Second Example: Private Data

function createAccount(startingBalance) {
  let balance = startingBalance; // not accessible from outside

  return {
    deposit(amount) { balance += amount; },
    getBalance() { return balance; }
  };
}

const account = createAccount(100);
account.deposit(50);
account.getBalance(); // 150 — balance itself is hidden from outside code

Common Mistakes

⚠ Watch out for
  • The classic var-in-a-loop bug: because var isn't scoped per iteration, closures created in a loop with var all end up sharing the same final value. Using let fixes this, since let creates a fresh variable each iteration.
  • Assuming closures copy a variable's value — they don't; they keep a live link to the actual variable, so later changes are visible too.
  • Creating closures unnecessarily in hot loops, which can hold onto memory longer than needed.
// The classic bug
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // logs 3, 3, 3
}
// Fixed with let
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // logs 0, 1, 2
}

Best Practices

  • Use closures to hide implementation details and expose only what's needed (encapsulation).
  • Prefer let/const over var so closures in loops behave as expected.
  • Don't over-nest functions purely to create closures — use them when they solve a real problem.

Performance Notes

Closures keep their captured variables in memory for as long as the closure itself is reachable, which can slightly increase memory use compared to plain variables that get garbage collected right away. This is rarely a real problem, but worth knowing for long-lived closures holding large data.

Browser Compatibility

Closures are a fundamental part of JavaScript and have worked identically in every browser since the language existed.

Interview Questions

What is a closure?

A function combined with references to the variables from the scope it was created in, allowing it to access those variables even after the outer function has finished running.

Give a practical use case for closures.

Creating private state, such as a counter or bank balance, that can only be read or changed through specific functions rather than direct access.

✏️ Exercise

Write a function makeGreeter(greeting) that returns a new function which takes a name and returns `${greeting}, ${name}!`. Create two greeters with different greetings and prove they don't interfere with each other.

🧠 Quiz

1. Does a closure store a copy of a variable, or a live link to it?

A live link — changes to the original variable are visible through the closure.

2. Why does using var instead of let in a loop cause bugs with closures?

var is function-scoped, not block-scoped, so every closure created in the loop shares the exact same variable instead of getting its own copy per iteration.

🚀 Mini Challenge

Build a createToggle() function that returns a function which flips between true and false every time it's called, starting at false.

Summary

  • A closure is a function that remembers variables from where it was defined.
  • Closures enable private state and are created automatically, with no special syntax.
  • Watch for the classic var-in-a-loop pitfall — use let instead.

Related Topics

MDN reference: developer.mozilla.org → JavaScript → Closures (placeholder — add live link)