JavaScript didn't have built-in modules for its first 20 years. Developers invented their own — first IIFE patterns, then CommonJS for servers, AMD for browsers, and finally ES Modules as the official standard. This article traces the evolution and implements each pattern.
Prerequisites: JS Foundations #3 — Closures
1. The IIFE Module Pattern (Pre-ES6)
Before any module system, the IIFE (Immediately Invoked Function Expression) created private scope and returned a public API:
Loading editor...
2. The Revealing Module Pattern
A cleaner variant that defines everything private then explicitly reveals what's public:
Loading editor...
3. CommonJS — The Node.js Standard
CommonJS uses require() and module.exports. Each file is wrapped in a function so it has its own scope:
Loading editor...
Key CommonJS traits:
- Synchronous loading —
require()is blocking - Single export object —
module.exportsis what you get - Copy of exports — you get a copy, not a live binding
4. AMD — Asynchronous Module Definition (Browser)
AMD was designed for browsers where loading files is async:
Loading editor...
5. ES Modules — The Official Standard
ESM is static — imports/exports are known at parse time, enabling tree-shaking:
Loading editor...
6. Comparing the Module Systems
Loading editor...
Key Takeaways
- IIFE: private scope via function closures — the precursor that proved modules were possible.
- CommonJS (
require/module.exports): synchronous, server-first, copies exports. - AMD (
define): async, browser-first, dependency array. - ESM (
import/export): static, tree-shakeable, live bindings, the modern standard. - ESM's static structure is its biggest advantage — bundlers can analyze and eliminate dead code at build time.
Next: Classes Under the Hood — desugar class, extends, super, and private fields to ES5.
