Closures and Lexical Scope
A closure is a function that captures variables from its surrounding lexical scope, preserving access to those variables after the outer function has returned.
Definition
A closure is created when a function references variables from an outer enclosing function's scope. The JavaScript engine preserves the outer function's variable environment in memory as long as the closure exists, even after the outer function has finished executing. Lexical scope means that variable visibility is determined by the physical nesting of functions in source code, not by the call stack at runtime. A closure 'closes over' (captures) the variables it references from outer scopes, maintaining their binding for the lifetime of the closure.
Context and scope
Closures are fundamental to JavaScript's module pattern, event handlers, callbacks, memoization, and functional programming utilities. They explain why an inner function can access outer variables long after the outer scope would normally have been garbage-collected. Every function in JavaScript forms a closure over the global scope; nested functions form closures over their containing function's scope.
Common misconceptions
- Closures copy variable values at creation time — they capture live references, so changes to the captured variable are visible inside the closure
- Only arrow functions create closures — all JavaScript functions create closures over their lexical environment
- Closures cause memory leaks by default — they only prevent GC of captured variables; if the closure itself is no longer reachable, collected variables are freed
Anti-patterns
- Creating functions inside a loop that reference the loop variable without capturing its current value (classic var-in-loop problem)
- Holding closures over large DOM trees or data arrays that are no longer needed
- Using closures to simulate private state when modern JavaScript provides private class fields (# prefix)
Related concepts
Authoritative source
What Are Closures and Lexical Scope
A closure gives a function persistent access to variables from an outer scope. Lexical scope determines which variables are visible where based on source code structure, not runtime call order.
How Lexical Scope Works
JavaScript resolves variable references by walking up the scope chain at the location where the function is defined — not where it’s called:
const greeting = 'global';
function outer() {
const greeting = 'outer';
function inner() {
console.log(greeting); // "outer" — resolved at definition site
}
return inner;
}
const fn = outer();
fn(); // "outer" (not "global")
Closures in Practice
Module Pattern
const counter = (function() {
let privateCount = 0; // captured by closure
return {
increment: () => ++privateCount,
getCount: () => privateCount,
};
})();
counter.increment();
console.log(counter.getCount()); // 1
// privateCount is not accessible directly
Function Factory
function createMultiplier(multiplier) {
return function(x) {
return x * multiplier; // captures 'multiplier'
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Common Pitfall: Loop Variable Capture
// ❌ Bug: all callbacks log '5'
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100);
}
// ✅ Fix: use let (block-scoped per iteration)
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // 0,1,2,3,4
}
When to Use Closures
- Encapsulating private state (module pattern)
- Creating specialized functions from general ones (currying, partial application)
- Preserving configuration across async callbacks
- Implementing memoization caches
Memory Considerations
Closures keep captured variables in memory. If a closure is long-lived, avoid capturing large objects that aren’t needed — set them to null when done.
Related Concepts
- Lexical Scope: Static scope resolution
- Garbage Collection: When captured variables become eligible for collection
- Private Class Fields: Modern alternative for encapsulation