All topics
Patternsintermediate

Module Pattern and Revealing Module Pattern

Using closures (often via an IIFE) to create private state and expose only a deliberately chosen public API.

The module pattern uses a closure to keep state private and return only what should be public.

The module pattern is like a magician's workshop with a single service window: all the actual tools, notes, and half-finished tricks (private variables/functions) stay locked inside the workshop, and the only things the public ever sees are the specific finished tricks (public methods) handed out through that one window, with zero way to reach past it into the workshop itself.

Key Concepts

1
    const counter = (function () {
      let count = 0;                       // private
      function increment() { count++; }
      function get() { return count; }
2
      return { increment, get };           // public
    })();
3
    counter.increment();
    counter.get();      // 1
    counter.count;      // undefined -- unreachable
4
The revealing variant is the same thing with all the functions defined first and the return listing what is exposed:
5
    const api = (function () {
      function privateHelper() {}
      function publicMethod() { privateHelper(); }
      return { publicMethod };          // the "reveal"
    })();
6
The advantage is that the return statement is a readable index of the public surface.
7
Why it mattered. Before ES modules there was no file-level privacy. A script's top-level var became a global, so every library needed this to avoid polluting the namespace and colliding with others.
var
8
jQuery, Underscore and every library of that era are built this way.
9
What replaced it. ES modules give the same privacy at file level, with static analysis on top:
10
    // counter.js
    let count = 0;                      // private -- not exported
    export function increment() { count++; }
    export function get() { return count; }
11
Same encapsulation, no IIFE, and a bundler can tree-shake it.
12
Where the pattern still appears. Bundled output wraps modules in exactly this shape, so you read it constantly. And a factory returning an object with closed-over state is the same idea:
13
    function createCounter() {
      let count = 0;
      return { increment: () => count++, get: () => count };
    }
14
That is the module pattern used deliberately — one instance per call, rather than a singleton.
15
The distinction worth making in an interview. The module pattern creates one instance immediately. A factory creates a new one per call. Both use closures for privacy; only the first is a singleton, and choosing the singleton by accident is a common mistake.
16
What the interviewer is probing.1. "What did the module pattern give you before ES modules?" Probing: the history. Stalls: "Organisation." Moves up: a private scope via a closure, exposing only what the returned object listed — the only encapsulation available.
17
2. "What does the revealing variant change?" Probing: the difference. Stalls: "Nothing." Moves up: everything is defined privately and the return object maps public names to them, so the public surface is one readable block.
18
3. "Is it still relevant?" Probing: currency. Stalls: "Yes, always use it." Moves up: rarely — ES modules give the same privacy at file level; it survives in bundled libraries and legacy code.
19
4. "What is the testing drawback?" Probing: the trade. Stalls: "None." Moves up: private functions cannot be tested or stubbed directly, so you test only through the public surface.