ES Modules
ES Modules
Enabling Modules
<script type="module" src="app.js"></script>
Key Features
- Strict mode by default
- Own scope (no global pollution)
- Asynchronous loading
- Tree-shaking support
Module Scope
// module.js
const private = 'not accessible globally';
export const public = 'accessible';
// app.js
import { public } from './module.js';
console.log(public); // "accessible"
// console.log(private); // Error!
Module vs Script
| Feature | Script | Module |
|---|---|---|
| Scope | Global | Module |
| Strict | Optional | Always |
| Import | N/A | Yes |
| Async | No | Yes |
| defer | Attribute | Default |
Import/Export
Import/Export
Named Exports
// utils.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export class User {
constructor(name) {
this.name = name;
}
}
// app.js
import { PI, add, User } from './utils.js';
console.log(PI, add(1, 2));
Default Exports
// user.js
export default class User {
constructor(name) {
this.name = name;
}
}
// app.js
import User from './user.js';
Renaming Imports
import { add as sum, PI as pi } from './utils.js';
Importing All
import * as Utils from './utils.js';
console.log(Utils.PI, Utils.add(1, 2));
Side-Effect Imports
import './styles.css';
Dynamic Imports
const module = await import('./module.js');
// In function
async function loadModule() {
const { default: Module } = await import('./module.js');
return new Module();
}
Module Patterns
Module Patterns
Re-Exporting
// index.js - barrel file
export { add, subtract } from './math.js';
export { User } from './user.js';
export { PI } from './constants.js';
// Usage
import { add, User, PI } from './index.js';
Module Organization
src/
modules/
auth/
index.js
login.js
logout.js
user/
index.js
User.js
utils/
index.js
helpers.js
app.js
Namespace Pattern
// constants.js
export const COLORS = {
primary: '#3b82f6',
secondary: '#10b981'
};
export const SIZES = {
small: 12,
medium: 16,
large: 24
};
// Usage
import { COLORS, SIZES } from './constants.js';
Configuration Module
// config.js
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3
};
export default config;
Circular Dependencies
// Avoid if possible
// a.js
import { b } from './b.js';
export const a = 'a';
// b.js
import { a } from './a.js'; // May fail!
export const b = 'b';
Best Practices
- One module per file
- Use named exports for multiple values
- Use default export for single main value
- Create barrel files for directories
- Avoid circular dependencies
- Import at the top of files
Quiz
1. How do you enable ES modules in HTML?
2. What is the difference between named and default exports?
3. How do you import everything from a module?
4. What is a barrel file?
5. What is a dynamic import?
Flashcards
Question
How do you export a value from a module?
Click to reveal answer
Answer
Use export keyword: export const value = 5; export default value;
Question
What is the difference between named and default export?
Click to reveal answer
Answer
Named: export { name }. Default: export default value. Multiple named, one default.
Question
What is a barrel file?
Click to reveal answer
Answer
An index.js that re-exports from a directory: export { name } from './file.js';
Question
What is dynamic import?
Click to reveal answer
Answer
Using import() function to load modules dynamically: const mod = await import('./mod.js');
Question
What are the benefits of ES modules?
Click to reveal answer
Answer
Strict mode, own scope, async loading, tree-shaking, no global pollution.
Revision Notes
Key Takeaways
- 1. ES modules provide native module support
- 2. Named exports allow multiple values per module
- 3. Default export is for the main value
- 4. Barrel files re-export from directories
- 5. Dynamic imports load modules on demand
Interview Tips
- • Know the difference between named and default exports
- • Understand module scope vs global scope
- • Be able to create barrel files
- • Know when to use dynamic imports
Cheat Sheet
ES Modules Cheat Sheet
Exporting
// Named export
export const name = 'John';
export function greet() { }
// Default export
export default class User { }
// Re-export
export { name } from './other.js';
Importing
// Named import
import { name, greet } from './module.js';
// Default import
import User from './module.js';
// Rename
import { name as n } from './module.js';
// All
import * as Module from './module.js';
// Dynamic
const mod = await import('./module.js');
HTML Setup
<script type="module" src="app.js"></script>
Best Practices
- One export per file (default)
- Barrel files for directories
- Avoid circular dependencies
- Import at top of file