intermediate · 21 minute study
A closure keeps access to its lexical environment
Closures power callbacks and private state, but also explain stale values, memory retention and confusing loop behavior.
Useful preparation · 1 prerequisite
Use this as a map, not a lock. If these ideas are familiar, continue.
- Two names can point to one object — Unexpected shared mutation breaks UI state, caches, saves and tests even when every line looks simple.
First, build a mental model
A function remembers where it was created. It retains access to bindings in that lexical environment even after the outer call has returned.
- Scope depends on where a function is defined, not which function calls it.
- Each factory invocation creates a separate environment.
- A closure observes its captured bindings; an explicitly computed snapshot may stay old.
- Long-lived callbacks can keep referenced objects reachable until the callback is released.
function counter() {
let count = 0;
const initial = count;
return { increment: () => ++count, current: () => count, snapshot: () => initial };
}
const a = counter();
const b = counter();
a.increment(); a.increment();
console.log(a.current(), b.current(), a.snapshot());- Create A
a's methods close over one count binding and one initial value.
- Create B
b has a separate environment.
- Increment
Only a's count changes; initial remains zero.
Production thinking
Take this beyond the example.
Give listeners, intervals, subscriptions and workers an owner and a cleanup path. Review closure capture when state, account or request scope changes.
Sources and further reading
Your evidence
unseen is a local learning signal, not a credential or proof of production competence.
Explain the mental model, then explicitly acknowledge it. Opening a page is not evidence.
Supported retries can complete this lesson. Independent evidence and delayed review determine the skill stage.
Next lesson · Method calls choose a receiver →