IIFE Closures in JavaScript

An Immediately Invoked Function Expression (IIFE) runs as soon as it is defined and can create a private lexical scope. If it returns a function that references variables from that scope, those variables remain accessible through a closure after the IIFE has finished running.

An IIFE is a function expression that is invoked immediately, commonly written as `(function () { ... })()`. Because the function creates its own lexical scope, variables declared inside it are not directly accessible from the surrounding scope.

A closure occurs when a function retains access to variables from its surrounding lexical environment after that surrounding function has returned. An IIFE and a closure therefore solve different parts of the problem: the IIFE creates the scope, while the returned function can preserve access to selected state inside that scope.

This pattern can be useful for encapsulating mutable state. For example, an IIFE can initialize a counter and return `increment` and `getValue` functions. Code outside the IIFE cannot directly access the counter variable, but the returned functions can read or modify it.

Modern JavaScript provides other ways to organize private state, including ES modules and private class fields. IIFEs are still useful to recognize because they appear in older codebases and clearly demonstrate lexical scoping and closures.

Key points

Interview questions

What is an IIFE, and why would you use one?

An IIFE is a function expression that is invoked immediately after it is defined. It creates a local lexical scope, so variables declared inside it do not directly leak into the surrounding scope. It can be used to isolate initialization logic or private state. Older JavaScript code often used IIFEs before modules were widely available.

How can an IIFE create private state with a closure?

The IIFE declares state in its local scope and returns a function that references that state. The returned function forms a closure over the variable. External code cannot directly name the local variable, but it can interact with it through the returned function. As long as the closure remains reachable, the captured state can remain available.

Does a closure store a copy of the captured variable?

Conceptually, a closure retains access to the binding in its surrounding lexical environment rather than simply receiving a frozen copy of its value. This is why multiple closures created in the same environment can observe changes to shared state. For example, an increment function can update a variable and a getter can subsequently observe that updated value. This behavior is important when reasoning about mutable closed-over state.

Why were IIFEs common before JavaScript modules?

Before ES modules became a standard way to provide module-level organization, scripts shared the global environment unless developers deliberately isolated their variables. An IIFE provided a function scope where implementation details could be kept private. Selected functions or values could then be exposed through the IIFE's return value. This reduced accidental global variables and namespace collisions.