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.

💡 The Workshop & Tool Room Analogy

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.

01

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.

02

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 with as.
  • 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);
Output
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" });
}
🚀 Performance Benefit: Dynamic imports enable Code Splitting in bundlers like Vite, Webpack, and Next.js, dramatically reducing the initial bundle size and speeding up website load times.

5. Critical Module Rules & Live Bindings

01

Automatic Strict Mode

All ES modules operate in "use strict" automatically. Undeclared variables and duplicate parameter names trigger instant syntax errors.

02

Top-level this is undefined

Unlike regular scripts where this === window, at the top level of an ES module, this evaluates to undefined.

03

Live Bindings (Not Copies!)

Imported variables are live references. If the exporting module updates a variable, importers immediately observe the new value.

Placement MCQ Quiz: ES Modules & Architecture
Question 1 of 5
JavaScript Interview PuzzleQ1 of 5
// In fileA.js:
export default function greet() { return "Hello from A"; }
export function sayBye() { return "Goodbye from A"; }

// In fileB.js:
import greet, { sayBye } from "./fileA.js";
console.log(greet());
console.log(sayBye());
What will be printed to the console?
ESM Live Bindings TrapQ2 of 5
// In counter.js:
export let count = 10;
export function increment() { count++; }

// In app.js:
import { count, increment } from "./counter.js";
console.log("Initial:", count);
increment();
console.log("After:", count);
What will be printed when app.js runs?
Top-level Scope in ES ModulesQ3 of 5
// In a script loaded with <script type="module">:
console.log(this);
What does this evaluate to at the top level of an ES module?
Static Import in Conditional BlocksQ4 of 5
const isDev = true;
if (isDev) {
    import { debugLog } from "./logger.js";
    debugLog("Running in dev mode");
}
What happens when this file is parsed by the JavaScript engine?
Default Export RenamingQ5 of 5
// In userService.js:
export default function authenticate() { return "Logged in!"; }

// In app.js:
import loginUser from "./userService.js";
console.log(loginUser());
What will be logged to the console?

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().

Your output will appear here.