Closure Lifetime
A closure can keep captured variables alive after the surrounding function has returned. The captured bindings remain available while a reachable closure still retains access to their lexical environment.
A closure is a function together with access to the lexical environment in which it was created. If the inner function references a variable from an outer scope, that variable may need to remain available after the outer function has finished executing.
Consider a function that creates and returns another function. The outer function's stack frame is no longer executing when the returned function is later called, but the captured binding is still accessible. JavaScript engines preserve the necessary environment rather than treating the variable as immediately unreachable.
The lifetime is determined by reachability, not simply by the duration of the function call that created the closure. As long as a closure that captures the environment remains reachable, the relevant captured state can remain alive. Once nothing reachable can access that environment, it can become eligible for garbage collection.
Captured state is shared according to the binding that was captured. Multiple closures created from the same invocation can therefore observe and modify the same captured variable. This differs from capturing a fresh independent value for every function unless the program creates separate lexical environments.
Key points
- A closure can outlive the function invocation that created it.
- Captured variables can remain alive because the closure retains access to their lexical environment.
- Garbage collection is based on reachability, so unreachable closure environments can eventually be reclaimed.
- Multiple closures from one lexical environment can share captured bindings.
- Closures are useful for private state, callbacks, event handlers, and factory functions.
Interview questions
What happens to a local variable after the function that declared it returns if an inner function captures it?
The variable can remain available after the outer function returns. The returned inner function has access to the lexical environment containing that variable. The environment stays reachable through the closure, so the captured state can be used later.
Why does this counter keep incrementing instead of resetting to zero?
`counter()` creates a lexical environment containing `value` and returns a function that references that binding. Each invocation of `counter()` creates a separate environment, but repeated calls to the same returned function access the same captured `value`. Therefore, `next()` observes the update made by its previous call.
How does closure lifetime relate to garbage collection?
A closure can keep its captured environment and objects reachable even after the creating function returns. Garbage collection can reclaim those resources when they are no longer reachable from live program roots. Consequently, creating a closure does not inherently create a memory leak. A leak can occur when an otherwise unnecessary closure remains reachable, such as through a long-lived listener or registry.
What is the difference between capturing a variable and capturing its current value?
Closures in JavaScript capture access to lexical bindings rather than generally freezing a primitive snapshot at function creation. If the binding is later reassigned, a closure reading that binding can observe the new value. The exact behavior also depends on how the variable is declared and where the closure is created, which is especially important in loops. This distinction explains many callback and loop bugs.