Module Pattern

Easy

The Module Pattern uses closures and IIFEs to create private scope, exposing only a public API. It is the foundation of JavaScript encapsulation and was the primary code-organization tool before ES modules became standard.

Interactive Visualization

Creational Patterns

IIFE creates private scope, returns public API via closure
returnscloses overcallsIIFE Scopeprivate varsPublic APIConsumer
1 / 3

Understanding Module Pattern

The Module Pattern is arguably the most influential design pattern in JavaScript history. Before ES2015 introduced native modules, it was the only reliable way to create truly private variables. The pattern leverages two fundamental JS features: closures and IIFEs. The IIFE executes immediately and returns an object, while the closure keeps the private variables alive in memory.

The Revealing Module Pattern is a refinement where all functions are written as private, and only the desired ones are mapped to the returned object. This makes it easy to see the public API at a glance, and switching a method from public to private requires only removing it from the return statement.

Even in modern codebases with ES modules, the Module Pattern remains relevant. It is used inside library entry points to expose a controlled API, in configuration objects that need runtime privacy, and in any situation where you want to guarantee that internal state cannot be accessed or mutated externally. Understanding this pattern is essential because it demonstrates mastery of closures, scope, and JavaScript's approach to encapsulation.

Key Points

  • Uses an IIFE (Immediately Invoked Function Expression) to create a private scope
  • Variables inside the IIFE are inaccessible from outside — true privacy
  • Returns an object literal exposing only the public API
  • The Revealing Module Pattern names all methods privately, then maps them in the return object
  • Closures keep private state alive across invocations
  • Predecessor to ES modules — still useful for library encapsulation and config objects
  • Can be extended with augmentation patterns for large codebases

Code Examples

Basic Module Pattern

const Counter = (function () {
  let count = 0; // private

  return {
    increment() { count++; },
    decrement() { count--; },
    getCount() { return count; },
  };
})();

Counter.increment();
Counter.increment();
console.log(Counter.getCount()); // 2
console.log(Counter.count);      // undefined — private!

The IIFE creates a closure. count is private; only the returned methods can access it.

Revealing Module Pattern

const AuthModule = (function () {
  let token = null;

  function login(user, pass) {
    // authenticate...
    token = 'jwt-' + btoa(user);
    return true;
  }

  function logout() { token = null; }

  function isAuthenticated() { return token !== null; }

  // Reveal public API
  return { login, logout, isAuthenticated };
})();

AuthModule.login('alice', 'secret');
console.log(AuthModule.isAuthenticated()); // true
console.log(AuthModule.token);             // undefined

All functions are defined privately, then selectively exposed in the return object for a clean API.

Module Augmentation

const App = (function (app) {
  app.utils = {
    formatDate(d) { return d.toISOString().slice(0, 10); },
    capitalize(s) { return s[0].toUpperCase() + s.slice(1); },
  };
  return app;
})(App || {});

// Another file can extend:
const App2 = (function (app) {
  app.logger = {
    log(msg) { console.log('[LOG]', msg); },
  };
  return app;
})(App || {});

Passing the module into a new IIFE lets multiple files contribute to the same namespace without conflicts.

Common Mistakes

  • Forgetting the wrapping parentheses around the IIFE — causes a syntax error
  • Returning references to mutable private objects, accidentally leaking state
  • Over-using the pattern when ES modules (import/export) are available
  • Not realizing that private methods cannot be tested directly

Interview Tips

  • Connect the pattern to closures — explain how the IIFE creates the closed-over scope
  • Mention that ES modules replaced this pattern for file-level encapsulation but it is still used for runtime privacy
  • Compare with class private fields (#) — both achieve encapsulation, different mechanisms

Related Concepts