Skip to main content

JavaScript Modules

JavaScript Module Patterns

Written by Published

Before real modules existed, JavaScript used closures to fake the same isolation.

This is worth knowing because it still appears throughout older code.

It is also a good way to see exactly what real modules give you for free.

Example

Example

javascript

const counterModule = (function () {
  let count = 0;
  return {
    increment: function () { count++; return count; },
    reset: function () { count = 0; }
  };
})();

console.log(counterModule.increment());
console.log(counterModule.increment());

The output is 1 then 2.

The Module Pattern

An IIFE creates a private scope, just like a real module file.

It returns an object exposing only what should be public.

Everything else stays hidden inside the closure.

Syntax

Syntax

javascript

const myModule = (function () {
  // private
  return { /* public */ };
})();

The brackets at the end call the function immediately.

Private State

count cannot be reached or changed from outside.

Example

Example

javascript

const counterModule = (function () {
  let count = 0;
  return {
    increment: function () { count++; return count; }
  };
})();

counterModule.increment();

console.log(counterModule.count);
console.log(typeof counterModule.count);

The output is undefined then undefined: count was never exposed.

The Revealing Module Pattern

Everything is declared privately first, then chosen names are revealed.

Example

Example

javascript

const mathModule = (function () {
  function double(n) { return n * 2; }
  function square(n) { return n * n; }
  function helper(n) { return n + 1; }

  return {
    double: double,
    square: square
  };
})();

console.log(mathModule.double(5));
console.log(typeof mathModule.helper);

helper exists privately but was never revealed.

Compared with a Real Module

A real module gives you this automatically, with no IIFE required.

Everything is private unless export says otherwise.

Example

Example

javascript

// The pattern, simulating a real module:
const mathModule = (function () {
  function double(n) { return n * 2; }
  return { double: double };
})();

// The real equivalent, written as a file:
// function double(n) { return n * 2; }
// export { double };

console.log(mathModule.double(10));

Both give the same isolation; the module system does it without the wrapper.

Namespacing

The pattern was also used to avoid clashing with other scripts on the page.

Example

Example

javascript

var MyApp = MyApp || {};

MyApp.utils = (function () {
  return {
    double: function (n) { return n * 2; }
  };
})();

console.log(MyApp.utils.double(4));

A single global, MyApp, held everything the page needed.

Why Real Modules Replaced This

No IIFE boilerplate is needed.

The browser handles loading order and caching automatically.

Example

Example

javascript

const oldWay = (function () {
  return { value: 42 };
})();

const newWay = { value: 42 };

console.log(oldWay.value === newWay.value);

Both reach the same result; only the ceremony differs.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>The Module Pattern</title>
</head>
<body>

  <h1>The Module Pattern</h1>

  <p id="out"></p>

  <script>
    const counterModule = (function () {
      let count = 0;
      return {
        increment: function () {
          count++;
          return count;
        },
        reset: function () {
          count = 0;
        }
      };
    })();

    counterModule.increment();
    counterModule.increment();
    counterModule.increment();

    document.getElementById("out").textContent = "Count: " + counterModule.increment();
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try reaching the private value:

Add console.log(counterModule.count) and see it is undefined.

Important Points

  • An IIFE creates a private scope, simulating a module.
  • Only what the returned object exposes is public.
  • The revealing module pattern declares everything privately, then exposes chosen names.
  • Real ES modules give this isolation without any wrapper.
  • The pattern still appears constantly in code written before modules existed.

Conclusion

The module pattern was a clever workaround for a real gap in the language.

Seeing it makes real modules feel less like magic and more like a shortcut.

You will still meet this pattern regularly in existing code.