ES Modules are file-based. Each module has its **own scope** — no global variables leak between modules. They run in **strict mode** automatically. Modules are **cached**: importing the same module twice returns the same instance. Circular dependencies are supported but require careful design.
1Understanding JavaScript Modules
ES Modules are file-based. Each module has its own scope — no global variables leak between modules. They run in strict mode automatically. Modules are cached: importing the same module twice returns the same instance. Circular dependencies are supported but require careful design.
Modules are deferred by default in browsers — they don't block HTML parsing. You don't need the 'defer' attribute on module scripts.
// Module scope isolation
// moduleA.js
const secret = 'not global'; // NOT accessible outside
export const publicValue = 42;
// main.js
import { publicValue } from './moduleA.js';
console.log(publicValue); // 42
// console.log(secret); // ReferenceError2Practical Example
Here is a real-world application of JavaScript Modules showing how it is used in production JavaScript code.
// Dynamic import (lazy loading)
async function loadAnalytics() {
if (userHasConsented) {
// Only imported when needed!
const { trackEvent } = await import('./analytics.js');
trackEvent('page_view');
}
}3Best Practices
Follow these guidelines when working with JavaScript Modules:
1. Use .js extension in import paths for compatibility
2. Avoid circular dependencies — they often indicate a design issue
3. Use type: module in Node.js package.json for ESM support
Tip: Modules are deferred by default in browsers — they don't block HTML parsing. You don't need the 'defer' attribute on module scripts.
// Module scope isolation
// moduleA.js
const secret = 'not global'; // NOT accessible outside
export const publicValue = 42;
// main.js
import { publicValue } from './moduleA.js';
console.log(publicValue); // 42
// console.log(secret); // ReferenceError