Closures power callbacks, event handlers, module patterns, memoisation and React hooks.
A closure is a function together with the variables from the scope where it was created. In JavaScript every function is a closure: when it runs later, it can still read and update the variables that were in scope at its definition, even after the outer function has returned.
function makeCounter() {
let count = 0;
return () => ++count;
}Each call to makeCounter() creates a fresh count and a function that closes over it. Nothing else can reach that variable — closures give you private state without classes.
Closures capture variables, not values. If the variable changes later, the closure sees the new value. That's the source of the classic loop bug:
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i));
// 3, 3, 3There is only one var i, and by the time the callbacks run it's 3. With let, each iteration gets a new binding, so you get 0, 1, 2.
Typical uses:
- Factories that configure a function:
const add5 = makeAdder(5). - Callbacks and handlers that need context from where they were set up.
- Memoisation: keep a cache in the enclosing scope.
- Once/debounce wrappers that track calls between invocations.
A closure keeps everything it references alive. A long-lived handler that closes over a large array keeps that array in memory — a common source of leaks, covered in the garbage-collection lesson.
Example
function makeCounter(start = 0) {
let count = start; // private to this counter
return {
increment: () => ++count,
reset: () => { count = start; },
};
}
const a = makeCounter();
const b = makeCounter(10);
a.increment(); a.increment();
console.log(a.increment(), b.increment()); // 3 11
function memoize(fn) {
const cache = new Map(); // lives as long as the returned function
return (n) => {
if (!cache.has(n)) cache.set(n, fn(n));
return cache.get(n);
};
}
const slowSquare = n => { console.log("computing", n); return n * n; };
const fastSquare = memoize(slowSquare);
fastSquare(4); // computing 4
console.log(fastSquare(4)); // 16 (cached, no log)Remember
- A closure is a function plus the variables in scope where it was defined
- Closures capture variables, not snapshots of values
- Use them for private state, factories and caches
Watch out for
- Closing over a
varloop variable in async callbacks — uselet.