HuntExams Academy logo
JavaScriptIntermediate#closures#functions

What is a closure?

Answer

A function bundled with the lexical scope it was created in, so it keeps access to those outer variables after the outer function returns. Closures power private state, memoization and function factories.

Example

function counter() {
  let count = 0;
  return () => ++count;
}
const inc = counter();
inc(); // 1
inc(); // 2

Detailed explanation

A closure is a function together with access to the lexical environment in which it was created. Lexical scope means that variable lookup depends on where functions are written in the source code, not where they are called. When a function uses a variable from an enclosing scope, it can continue accessing that variable even after the enclosing function has finished executing. Returning an inner function is a common demonstration, but closures also occur in callbacks and other nested functions without an explicit return.

In the counter example, calling counter() creates a local count binding initialized to zero and returns an arrow function that increments it. Assigning that function to inc keeps the function—and access to its surrounding environment—reachable. The first inc() call changes count to one, and the second changes the same binding to two. The important detail is that a closure retains access to a variable binding, not a frozen copy of its original value. Calling counter() again creates a separate environment, so another returned increment function would maintain an independent count.

Under the hood, JavaScript functions have an internal connection to their creation environment. During execution, identifiers are resolved through the chain of lexical environments until the appropriate binding is found. The engine preserves the state needed by reachable closures; it does not simply keep the entire outer function running or retain its call-stack frame. Actual storage and optimizations are engine-dependent. Once a closure and its retained state are no longer reachable, they become eligible for garbage collection, unless other references still keep them alive.

Closures matter because they let functions carry state without exposing that state as a global variable. They support private state, function factories, memoization caches, and event handlers that remember surrounding context. Interviewers typically check whether you can trace changing state, distinguish shared bindings from independent environments, and explain loop behaviour: var commonly gives callbacks one shared loop binding, whereas let in a for loop creates per-iteration bindings. Strong answers also recognise that long-lived callbacks can retain unnecessary data and that closures do not automatically freeze values or make asynchronous operations execute in order.

Key points to remember

  • A closure combines a function with access to its creation-time lexical environment.
  • Captured variables remain accessible after the enclosing function returns.
  • Closures access bindings, so later updates can be observed rather than frozen snapshots.
  • Separate counter() calls create independent state; closures from the same call can share state.
  • Common uses include private state, memoization, function factories, and callbacks.

Common mistakes candidates make

  • Saying closures copy outer variable values instead of retaining access to their bindings.
  • Assuming a closure exists only when an inner function is returned.
  • Expecting var-based loop callbacks to receive separate bindings for each iteration.

Likely follow-up questions

What happens if two functions close over the same count variable?

Both functions access the same binding, so an update made by one is visible to the other. For example, increment and reset functions returned by one factory call can operate on the same private counter.

Why does let fix the classic closure problem in asynchronous loops?

A for loop declared with let creates a distinct binding for each iteration, which that iteration's callback can capture. With var, callbacks share one loop binding and, if they run after the loop finishes, observe its final value.

Can closures cause memory leaks?

A reachable closure can keep captured objects reachable even when the application no longer needs them. Removing unused listeners or clearing timers can release retaining references, allowing garbage collection when no other references remain.

Interview tip: Define lexical scope first, trace the two inc() calls, and then explain why a second counter() call has independent state.

Related Interview Questions

1
JavaScriptBeginner#operators#coercion

Explain == vs ===.

Open
2
JavaScriptBeginner#coercion

What are truthy and falsy values?

Open
3
JavaScriptIntermediate#this#functions

How does 'this' work in JavaScript?

Open