Shared vs Independent Closure State

Closures created from the same lexical environment can observe and mutate the same captured bindings, while separately created environments give each closure set its own state. The distinction is especially important when factory functions are used to create reusable stateful functions.

A closure is a function together with access to variables from its surrounding lexical environment. The function does not necessarily receive a copied value; it retains access to the binding, so mutations can be observed by other closures that reference that same binding.

If two closures are defined in the same invocation of an outer function, they can share captured state. For example, a getter and setter-like function can both access one `count` binding, so calling one changes what the other observes.

A factory function creates a fresh lexical environment each time it is invoked. Closures returned from separate factory invocations therefore capture different bindings, even when the source code is identical. Updating state through one returned function does not affect state belonging to another factory invocation.

This distinction is useful in frontend code for encapsulating component-local utilities, counters, caches, event handlers, and other stateful behavior. When debugging, ask whether functions came from the same environment creation or from separate calls to the function that creates that environment.

Key points

Interview questions

When do two closures share state?

They share state when they retain access to the same lexical environment and therefore the same captured bindings. This commonly happens when multiple functions are created during one invocation of an outer function. If one closure mutates a captured variable, another closure using that same binding can observe the change. Separate invocations of the outer function generally create separate environments.

How would you create independent counters with closures?

I would put the counter variable inside a factory function and return a function that accesses it. Every invocation of the factory creates a new lexical environment and therefore a new counter binding. Calling one returned counter changes only the state captured by that instance. This gives each consumer isolated state without exposing the variable directly.

Can two different functions returned from one factory share private state?

Yes. If the factory declares one variable and returns multiple functions that reference it, those functions retain access to the same binding. A mutation performed by one function is visible when another function reads the binding. This is a common closure-based pattern for encapsulating state behind a small API.

Why don't two calls to the same closure factory normally share captured variables?

A function invocation creates a new execution context and corresponding lexical environment for its local bindings. When the factory is called again, its local state is therefore a different binding from the state created by the previous call. Closures returned by the two invocations retain references to their respective environments. Consequently, mutating a binding through one closure does not mutate the corresponding binding in the other environment.

What is the practical difference between sharing closure state and creating independent closure state in frontend code?

Shared closure state can coordinate several functions or operations that intentionally belong to one logical instance. Independent closure state lets the same behavior be instantiated repeatedly without instances interfering with each other. A frontend engineer should decide which behavior is intended before choosing where the mutable binding is created. This matters for reusable utilities, event handlers, caches, and per-component state.