Closures, With Real Uses
A function remembering the variables it was born in -- the engine behind privacy, memoisation and stateful callbacks.
Syntax
function outer() { let x; return () => x; }A closure is a function bundled together with the variables that were in scope when it was defined. The inner function keeps those variables alive even after the outer function has returned.
function counter() {
let n = 0;
return () => ++n; // this arrow closes over n
}
const next = counter();
next(); next(); // 1, 2 -- n lives on, private and unreachable from outsideWhy it matters in real code
- Private state -- data no other code can touch, without a class.
- Memoisation -- cache results in a closed-over map.
- Factories -- build configured functions like
multiplyBy(3).
The famous loop bug
A var in a loop is shared by every closure, so all callbacks see the final value. let creates a fresh binding per iteration and fixes it -- one of the best reasons to have retired var.
Example
Loading editor…
Press Run to execute the code.
When to use it
- Create a private counter that can only be incremented through an exposed function, not directly.
- Capture a loop variable's value per iteration for async callbacks using a closure.
- Build a memoisation wrapper that caches previous return values in a closed-over Map.
More examples
Private counter via closure
The returned methods close over count, which is inaccessible from outside makeCounter.
function makeCounter() {
let count = 0;
return { increment: () => ++count, value: () => count };
}
const c = makeCounter();
c.increment();
c.increment();
console.log(c.value()); // 2Capture loop variable with IIFE
Wraps each loop body in an IIFE so each closure captures its own copy of i.
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push((x => () => x)(i)); // IIFE captures i per iteration
}
console.log(fns[0](), fns[1](), fns[2]()); // 0 1 2Memoisation with a closed-over cache
The inner function closes over cache so results persist between calls without a global variable.
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
const slowDouble = memoize(n => n * 2);
console.log(slowDouble(5)); // 10 – computed
console.log(slowDouble(5)); // 10 – cached
Discussion