Engrain by ByteRush

JS.02.03 Junior· 10 minCore — asked often

Closures: functions that remember where they were made

A closure is a function plus the variables around it. Use closures for private state, understand the var-in-a-loop trap, and write once().

Comes up in: JavaScript, Coding, Recruiter screen

Learn#

Deepa builds a “Pay now” button. Users double-click, and some are charged twice. She needs a function that can run only once — which means a function that remembers whether it already ran.

Functions in JavaScript remember. A function keeps a link to the scope it was created in, and can use those variables even after that scope’s function has returned. A function together with those remembered variables is a closure.

That gives you private state: data only the returned function can reach.

function makeCounter() {
  let count = 0;
  return () => ++count;
}
const next = makeCounter();
console.log(next(), next(), next());

for (var i = 0; i < 3; i++) setTimeout(() => console.log("var:", i));
for (let j = 0; j < 3; j++) setTimeout(() => console.log("let:", j));

Output (verified)

1 2 3
var: 3
var: 3
var: 3
let: 0
let: 1
let: 2

The loops show the famous trap. var i is one variable for the whole function, so all three callbacks remember the same i, which is 3 by the time they run. let j creates a new variable for each loop turn, so each callback remembers its own.

The counter remembers countStep 1 of 4

makeCounter scope

  • count = 0

Call stack

  • makeCounter

Console

    empty

    makeCounter runs and creates count = 0 in its scope.

    Read the steps as text
    1. makeCounter runs and creates count = 0 in its scope.
    2. It returns the arrow function. makeCounter's call ends, but the arrow still links to its scope.
    3. next() runs the arrow. It finds count through its saved link and adds 1.
    4. Each call changes the same count. Nothing outside can reach it.

    Predict#

    Predict first

    Two counters: const a = makeCounter(); const b = makeCounter(); a(); a(); What does b() return?

    Interview angle#

    Say it like this — about 30 seconds

    “A closure is a function plus the variables of the scope where it was created. The function keeps a reference to that scope, so it can read and change those variables after the outer function has returned. I use closures for private state — counters, once, memoize caches — and for callbacks that need local data. The classic trap is var in a loop: all callbacks share one variable. let fixes it by creating a new binding each iteration.”

    Stays on your device. Nothing is uploaded.
    Follow-up What does for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) print, and how do you fix it?
    3, 3, 3. Fix it with let i, with an IIFE that takes i as a parameter, or by passing i as an extra argument to setTimeout.
    Follow-up Implement a function that can be called only once.
    Keep called and result in a closure; on the first call run fn and store the result; later calls return the stored result.
    Follow-up What is a stale closure in React?
    A callback or effect that remembers state from an old render, so it reads outdated values (RCT.04.03).

    Practice#

    On your own

    Write once(fn) — the fix for Deepa’s double charge.

    Done when

    • fn runs on the first call only; later calls return the first result.
    • Arguments and this reach fn.
    • Two once wrappers do not share state.
    Your solution
    Ctrl/⌘ + Enter

    Remember#

    Try to answer each card before you open it. Then add them to your reviews.

    What is a closure?

    A function together with the variables of the scope where it was created.

    What does this print?
    for (var i = 0; i < 3; i++) setTimeout(() => console.log(i));
    3
    3
    3
    Why does let fix the loop-and-setTimeout trap?

    let creates a new binding for each loop iteration.

    Name two practical uses of closures.

    Private state (counters, once) and caches (memoize); also callbacks that need local data.

    Go deeper

    Four layers. Stop at the one your interview needs; each ends with a quick check.

    The core ideaEvery round

    A closure keeps the variable, not a copy of its value. If the variable changes later, the closure sees the new value. That is both why counters work and why the var loop prints 3 three times.

    Check yourself: Does a closure copy the variable's value or keep the variable itself?

    It keeps the variable itself, so it sees later changes.

    How it works underneathSenior rounds

    When a function is created, the spec stores the current environment in its [[Environment]] slot. Each call creates a new environment whose outer link is that saved one Source: ECMA-262: OrdinaryFunctionCreate. For let loops, the spec copies the loop variable into a fresh environment each iteration Source: ECMA-262: CreatePerIterationEnvironment.

    Check yourself: Where does a function store the scope it was created in?

    In its internal [[Environment]] slot.

    Edge cases and trapsWhere people slip

    Before modules, libraries used an IIFE — an immediately invoked function expression, (function () { … })() — to keep their variables private and expose only what they returned. That is the “module pattern”. Today ES modules give each file its own scope, so IIFEs are rarely needed.

    Check yourself: What is an IIFE?

    A function expression that is called immediately, creating a private scope.

    In productionStaff-level answers

    React components are functions that run on every render, and each render’s handlers close over that render’s state. A timer or effect that keeps an old handler reads old state — a stale closure. The fixes are updater functions, correct effect dependencies, or refs (RCT.04.03).

    Check yourself: Why can an event handler created in an old React render show old state?

    It is a stale closure: it remembers the variables from the render that created it.

    Finished the five steps? Mark it, and its cards join your reviews.

    Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

    Interview questions this answers

    • What is a closure? Give a practical use.
    • What will for (var i=0;i<3;i++) setTimeout(()=>console.log(i)) print? How do you fix it?
    • Implement a function that can be called only once.