ES6 Modules: The Linking Phase
Master the native module system of JavaScript. Understand how the engine uses Module Records and the three-stage loading process (Construction, Instantiation, and Evaluation) to build robust applications.
Beyond Script Tags
Before ES6, JavaScript lacked a native module system, forcing developers to rely on global variables or third-party wrappers like CommonJS. **ES Modules (ESM)** introduced a standardized way to scope code, ensuring that variables remain private to their file unless explicitly `export`-ed.
// Architectural Logic: Encapsulation via Modules
// auth.js
const privateKey = '7f8...'; // Private to this module scope
export const login = (user) => {
/* ... logic ... */
return { status: 'authenticated' };
};
// app.js
import { login } from './auth.js';
console.log(login('admin')); // Works
// console.log(privateKey); // ReferenceError: privateKey is not definedThe Module Record & Linking
When you load a module, the JavaScript engine doesn't just "run" it. Instead, it creates a **Module Record**—a data structure that maps out all imports and exports. This leads to a critical distinction between ESM and CommonJS: **Static Analysis**.
Because the engine knows the dependency graph before a single line of code executes, tools like Webpack or Vite can perform **Tree Shaking** (removing unused code) with 100% accuracy.
// Technical Insight: The Linking Phase
// ES Modules are statically analyzed BEFORE execution.
// 1. Construction: Fetching and parsing source into Module Records.
// 2. Instantiation (Linking): Mapping imports/exports in memory.
// 3. Evaluation: Executing the code to fill those memory locations.
import { data } from './provider.js';
// Even if 'provider.js' hasn't "executed" yet, the 'data' binding
// is established during the linking phase.Browser Mechanics: `type="module"`
Loading a module in HTML via <script type="module"> triggers several unique browser behaviors:
- **Auto-Deferred:** Modules never block HTML parsing. They are executed after the DOM is ready, similar to the `defer` attribute.
- **Strict Mode:** All modules operate in `use strict` by default; you cannot opt-out.
- **CORS Requirement:** Modules fetched from other domains must have valid Cross-Origin Resource Sharing headers.
- **Singleton Execution:** No matter how many times a module is imported, its code runs exactly once.
Technical Insight: Live Bindings
Unlike CommonJS where exports are copies of values, ES Module exports are **Live Bindings**. If a module changes a value it exported, the importing module sees that change instantly. However, the importer cannot modify the value—exports are **read-only** to the consumer.
Module Checklist:
- ✅ **Encapsulation:** Keep logic local to the module to avoid global namespace pollution.
- ✅ **Static Imports:** Use top-level `import` for dependencies to enable tree-shaking.
- ✅ **Naming:** Use clear, descriptive names for exports to improve developer experience.
- ✅ **Refactoring:** Break large files into smaller, focused modules (Single Responsibility Principle).
- ✅ **Security:** Remember that ESM triggers stricter browser security (CORS).