Closures and Asynchronous Callbacks

A closure is a function that retains access to variables from its surrounding lexical scope, even when the function runs later. This makes closures especially useful for asynchronous callbacks in frontend code.

A closure is created when a function is defined inside a scope and refers to variables from that surrounding scope. The inner function can continue to access those variables after the outer function has finished executing.

Asynchronous APIs commonly accept callbacks that run later, such as after a timer, network request, or user interaction. Because the callback is a closure, it can retain the state it needs from the scope where it was created.

The captured variable is not necessarily copied as a simple value at creation time. In JavaScript, closures preserve access to the relevant lexical environment, so later changes to a captured variable can be observable when the callback executes.

Closures are useful for event handlers, timers, promise callbacks, and functions that need private state. They can also accidentally retain objects longer than intended, so unnecessary references in long-lived callbacks should be avoided.

Key points

Interview questions

What is a closure in JavaScript?

A closure is a function together with access to variables from its surrounding lexical scope. The function can continue using those variables after the outer function has returned. This works because the referenced lexical environment remains available while the function needs it. Closures are common in callbacks and functions that maintain private state.

Why are closures useful for asynchronous callbacks?

An asynchronous callback may run after the function that created it has already returned. A closure lets that callback retain access to variables from its original lexical scope. For example, a timer callback can use configuration or state defined by the function that scheduled it. This allows asynchronous code to carry the context it needs without putting that state in global variables.

Does a closure capture a value or a variable?

Conceptually, JavaScript closures retain access to bindings in their lexical environment rather than simply taking an independent snapshot of every value. Therefore, if a captured binding is changed before the closure executes, the closure can observe the updated value. This distinction is important when multiple callbacks share mutable state. The exact behavior should be reasoned about from the binding and scope involved.

How can closures contribute to memory leaks in frontend applications?

A closure can keep references to objects reachable from its retained lexical environment. If the callback itself remains reachable through a timer, event listener, subscription, or other long-lived object, those referenced objects may remain reachable as well. This is not inherently a leak, because retaining state can be intentional, but failing to remove long-lived listeners or cancel unnecessary asynchronous work can cause unwanted retention. Cleanup should therefore match the lifetime of the callback and the resources it references.