JavaScript
Master ES Modules (ESM) vs CommonJS (CJS), named/default exports, dynamic imports, live bindings, and tree-shaking.
1. What are JavaScript Modules?
Before modules existed, every JavaScript file dumped its variables straight into one giant global object (window). If two scripts defined let user = ..., one would silently overwrite the other and crash your app.
A Module is simply an isolated JavaScript file with its own private scope. Nothing leaks out unless you explicitly declare export, and nothing gets in unless you write import.
Think of each module as an independent workshop room:
- Private Workbench (Module Scope): Tools and notes on the table inside the room are completely invisible to other rooms.
- The Display Window (
export): You choose specific finished tools to place in the window for others to borrow. - The Requisition Order (
import): Another workshop requests those specific tools by name through the front counter.
2. ESM (Modern Standard) vs CommonJS (Legacy Node.js)
In the JavaScript ecosystem, you will encounter two primary module systems: ES Modules (ESM) which is the official ECMAScript standard used in modern browsers and React, and CommonJS (CJS) which was created for Node.js.
ES Modules (ESM) — Modern Standard
Uses import / export. Analyzed statically at compile-time before code executes, enabling tree-shaking (dead-code elimination) and asynchronous loading.
CommonJS (CJS) — Legacy Node.js
Uses require() / module.exports. Evaluated dynamically and synchronously at runtime. Does not support static tree-shaking.
| Feature | ES Modules (ESM) 🌟 | CommonJS (CJS) 📦 |
|---|---|---|
| Keywords | import / export |
require() / module.exports |
| Loading Timing | Static / Pre-parsed before execution | Dynamic / Synchronous at runtime |
| Tree Shaking | ✅ Yes (Bundlers can remove unused exports) | ❌ No (Entire module object is imported) |
| Top-Level Await | ✅ Supported natively | ❌ Not supported directly at top level |
| Default Environment | Modern Browsers, React, Vite, Next.js, Node (with "type": "module") |
Classic Node.js scripts (.js or .cjs) |
3. Named Exports vs Default Exports
JavaScript provides two distinct ways to share values between files:
- Named Exports: Can export multiple values per file. When importing, names must match exactly inside curly braces
{ }, or be aliased withas. - Default Export: Can only have one default export per file. Imported without curly braces, and you can name it whatever you like.
// 📁 mathUtils.js
// 1. Multiple Named Exports
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
// 2. Single Default Export
export default class Calculator {
multiply(a, b) { return a * b; }
}
// 📁 app.js
// Importing default and named exports together:
import Calculator, { add, subtract as minus, PI } from "./mathUtils.js";
// Wildcard / Namespace import (imports everything into one object):
import * as MathToolkit from "./mathUtils.js";
const calc = new Calculator();
console.log("Add:", add(10, 5));
console.log("Minus (Aliased):", minus(10, 4));
console.log("Multiply via Default:", calc.multiply(6, 7));
console.log("Via Namespace:", MathToolkit.PI);
Add: 15 Minus (Aliased): 6 Multiply via Default: 42 Via Namespace: 3.14159
4. Dynamic Imports: import() for Lazy Loading
Standard import ... from '...' statements are static and must sit at the top level of the file. However, if you have a heavy library (e.g. PDF generator, 3D chart, or admin panel) that is only needed when a user clicks a button, use the function-like dynamic import().
// 📁 reportViewer.js
async function handleDownloadReport() {
console.log("User clicked generate report...");
// Only downloads and executes pdfMaker.js when this function runs:
const { generatePdf } = await import("./pdfMaker.js");
generatePdf({ title: "Annual Report 2026", status: "Success" });
}
5. Critical Module Rules & Live Bindings
Automatic Strict Mode
All ES modules operate in "use strict" automatically. Undeclared variables and duplicate parameter names trigger instant syntax errors.
Top-level this is undefined
Unlike regular scripts where this === window, at the top level of an ES module, this evaluates to undefined.
Live Bindings (Not Copies!)
Imported variables are live references. If the exporting module updates a variable, importers immediately observe the new value.
Interactive Practice Challenge
Simulate a secure module pattern in JavaScript by building an encapsulated UserAuthModule with private state (currentUser, isLoggedIn) and public methods: login(username), logout(), and getStatus().