JavaScript Closures: Real-World Examples
Closures are not just an interview question. Here are patterns you actually ship.
Private State Without Classes
``js
function createCounter() {
let count = 0;
return {
increment() { count++; },
get() { return count; }
};
}
`
The count variable is unreachable from outside — true encapsulation, no #private syntax needed.
Memoization
`js
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (!cache.has(key)) cache.set(key, fn(...args));
return cache.get(key);
};
}
`
The cache survives between calls because it lives in the closure.
Event Handler with Captured Context
`js
function attachAnalytics(buttonEl, eventName) {
buttonEl.addEventListener('click', () => track(eventName));
}
`
Each handler remembers its own eventName without globals.
Module Pattern
Before ES modules, the module pattern used closures to namespace:
`js
const Logger = (() => {
let level = 'info';
return {
setLevel(l) { level = l; },
log(msg) { if (priority(level)) console.log(msg); }
};
})();
`
The for-loop var Trap
`js
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 3, 3, 3
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 0, 1, 2
}
`
let` creates a new binding per iteration, fixing the classic closure-over-loop-variable bug.
Closures power most utility libraries. Pair them with [destructuring tricks](/blog/destructuring-tricks) for cleaner factory functions.