JavaScript Closures
Functions that remember variables from their enclosing scope
Overview
A closure is created when a function accesses variables from an outer function's scope, even after the outer function has returned. Closures power private state, factories, and callbacks.
Syntax / Usage
function makeCounter(start = 0) {
let count = start
return {
increment() { count += 1; return count },
decrement() { count -= 1; return count },
value() { return count },
}
}
const counter = makeCounter(10)
counter.increment() // 11
counter.value() // 11
// Closure in loop (use let)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100) // 0, 1, 2
}
Examples
Memoize expensive calculations:
function memoize(fn) {
const cache = new Map()
return (...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 fib = memoize((n) =>
n < 2 ? n : fib(n - 1) + fib(n - 2)
)
Event handler with partial application:
function onClick(id) {
return () => console.log('Clicked', id)
}
buttons.forEach((btn) => {
btn.addEventListener('click', onClick(btn.dataset.id))
})
Common Mistakes
- Classic
varin loops creating closures that all see the final value - Memory leaks from closures holding large objects longer than needed
- Assuming
thisinside closures follows outerthis—arrow functions use lexicalthis - Debugging stale closures when state updates rely on old captured values
See Also
functions promises async-await array-methods