Stop 04 · JavaScriptTopics · 7/36

Closures

Part of the Frontend Developer Roadmap

In short

A function in JavaScript carries a little backpack of the variables around it. That backpack is a closure, and it powers counters, private data and a lot of how React hooks behave.

A function that 'remembers' variables from the scope it was created in, even after that outer function has finished running — the basis of private state and many patterns. Closures allow functions to retain access to lexical state and are central to callbacks, factories, event handlers, encapsulation, memoization, and asynchronous code.

The jaadu mascot rides a bicycle carrying a tray of tiffin boxes with painted lids.

Think of it like this

The dabbawala's code

A dabbawala picks up your tiffin in the morning. Hours later, across the city, he still knows exactly whose it is because the code is painted on the lid. A closure is a function carrying its home's variables with it, long after it left home.

Watch it happen

The counter that remembers

makeCounter
count
tapCounter

let count = 0

1/6makeCounter runs and creates a variable called count.

The big ideas

What you need to know

Functions remember

A function keeps access to the variables of the place it was created, even after that outer function has finished.

It's a live link, not a copy

If the remembered variable changes, the function sees the new value. It doesn't freeze a snapshot.

Private state for free

Variables inside the outer function can't be touched from outside. Only the returned function can change them.

Each call makes a fresh backpack

Call the outer function twice and you get two separate closures with their own variables.

See it in code

Walk through the code

js
1function makeCounter() {2  let count = 0;              // private, nobody outside can touch it3  return () => {4    count++;5    return count;6  };7}8 9const tapCounter = makeCounter();10console.log(tapCounter()); // 111console.log(tapCounter()); // 212 13const another = makeCounter();14console.log(another());    // 1 (its own separate count)

1/4count is created inside makeCounter, so nothing outside can touch it.

makeCounter has finished running, yet count lives on because the returned arrow function still needs it. That surviving link is the closure.

Save yourself the pain

Common mistakes

Oops

Using var in a loop with setTimeout and getting the last value every time.

Do this instead

Use let. It creates a fresh variable for each loop round, so each callback remembers its own value.

Oops

Thinking a closure saves the value at the moment it was created.

Do this instead

It holds a reference to the variable. Change the variable later and the closure sees the change.

Your mission

Build a once() helper

~20 min

Quick check

What does this log? for (var i = 0; i < 3; i++) setTimeout(() => console.log(i), 0);

Go deeper

  1. 1Write a counter function that returns an increment function using a closure. Validate your understanding by applying it in a small practical example.
  2. 2Explain in your own words why closures 'remember' outer variables. Validate your understanding by applying it in a small practical example.
  3. 3Spot closures in real code — debounce, memoization and module patterns all use them.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge