Modules (import/export)
ES6 modules allow you to organize code into separate files and share functionality between them.
Basic Export/Import
javascript// math.js - Named exports export function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; } export const PI = 3.14159; // Alternative syntax function multiply(a, b) { return a * b; } function divide(a, b) { return b !== 0 ? a / b : null; } export { multiply, divide }; // main.js - Named imports import { add, subtract, PI } from './math.js'; import { multiply, divide } from './math.js'; console.log(add(5, 3)); // 8 console.log(PI); // 3.14159 // Import with alias import { add as sum, subtract as diff } from './math.js'; console.log(sum(10, 5)); // 15
Default Exports
javascript// calculator.js - Default export class Calculator { constructor() { this.result = 0; } add(num) { this.result += num; return this; } getValue() { return this.result; } } export default Calculator; // utils.js - Default export with named exports export default function formatNumber(num) { return num.toLocaleString(); } export function isEven(num) { return num % 2 === 0; } export function isOdd(num) { return num % 2 !== 0; } // app.js - Default imports import Calculator from './calculator.js'; import formatNumber, { isEven, isOdd } from './utils.js'; const calc = new Calculator(); const result = calc.add(100).getValue(); console.log(formatNumber(result)); // 100 console.log(isEven(result)); // true
Dynamic Imports
javascript// Dynamic import with async/await async function loadModule() { try { const mathModule = await import('./math.js'); console.log(mathModule.add(5, 3)); // 8 // Default export from dynamic import const { default: Calculator } = await import('./calculator.js'); const calc = new Calculator(); console.log(calc.add(10).getValue()); // 10 } catch (error) { console.error('Failed to load module:', error); } } // Conditional loading async function loadFeature(featureName) { if (featureName === 'advanced') { const module = await import('./advanced-features.js'); return module.default; } else { const module = await import('./basic-features.js'); return module.default; } } // Dynamic import with Promise import('./math.js') .then(module => { console.log(module.PI); // 3.14159 }) .catch(error => { console.error('Import failed:', error); });
Module Patterns
javascript// config.js - Configuration module const config = { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3 }; export default config; // api.js - API module import config from './config.js'; class ApiClient { constructor() { this.baseUrl = config.apiUrl; this.timeout = config.timeout; } async get(endpoint) { const response = await fetch(`${this.baseUrl}${endpoint}`); return response.json(); } async post(endpoint, data) { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); } } export default new ApiClient(); // Export instance // services/userService.js - Service module import api from '../api.js'; export async function getUser(id) { return api.get(`/users/${id}`); } export async function createUser(userData) { return api.post('/users', userData); } export async function updateUser(id, userData) { return api.put(`/users/${id}`, userData); } // main.js - Using services import { getUser, createUser } from './services/userService.js'; async function main() { const user = await getUser(1); console.log('User:', user); const newUser = await createUser({ name: 'John', email: 'john@example.com' }); console.log('Created:', newUser); }
Re-exports and Barrel Exports
javascript// components/Button.js export default function Button({ text, onClick }) { return `<button onclick="${onClick}">${text}</button>`; } // components/Input.js export default function Input({ placeholder, value }) { return `<input placeholder="${placeholder}" value="${value}">`; } // components/Modal.js export default function Modal({ title, content }) { return `<div class="modal"><h2>${title}</h2><p>${content}</p></div>`; } // components/index.js - Barrel export export { default as Button } from './Button.js'; export { default as Input } from './Input.js'; export { default as Modal } from './Modal.js'; // Alternative barrel export syntax import Button from './Button.js'; import Input from './Input.js'; import Modal from './Modal.js'; export { Button, Input, Modal }; // app.js - Clean imports import { Button, Input, Modal } from './components/index.js'; // utils/index.js - Mixed exports export * from './math.js'; // Re-export all named exports from './calculator.js'; export { formatNumber as format } from './formatters.js';
Module Loading and Bundling
javascript// Feature detection and polyfills async function loadPolyfills() { const promises = []; if (!window.fetch) { promises.push(import('./polyfills/fetch.js')); } if (!Array.prototype.includes) { promises.push(import('./polyfills/array-includes.js')); } await Promise.all(promises); } // Lazy loading components class ComponentLoader { constructor() { this.cache = new Map(); } async load(componentName) { if (this.cache.has(componentName)) { return this.cache.get(componentName); } try { const module = await import(`./components/${componentName}.js`); const component = module.default; this.cache.set(componentName, component); return component; } catch (error) { console.error(`Failed to load component ${componentName}:, error`); throw error; } } } const loader = new ComponentLoader(); // Usage async function renderComponent(name, props) { const Component = await loader.load(name); return Component(props); } // Module federation pattern const moduleRegistry = new Map(); export function registerModule(name, moduleFactory) { moduleRegistry.set(name, moduleFactory); } export async function getModule(name) { if (moduleRegistry.has(name)) { const factory = moduleRegistry.get(name); return await factory(); } // Fallback to dynamic import return await import(`./modules/${name}.js`); } // Register modules registerModule('analytics', () => import('./analytics.js')); registerModule('payments', () => import('./payments.js'));
CommonJS vs ES Modules
javascript// CommonJS (Node.js traditional) // math-commonjs.js function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } module.exports = { add, subtract }; // app-commonjs.js const { add, subtract } = require('./math-commonjs.js'); console.log(add(5, 3)); // 8 // ES Modules (modern) // math-esm.js export function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; } // app-esm.js import { add, subtract } from './math-esm.js'; console.log(add(5, 3)); // 8 // Interoperability in Node.js // package.json { "type": "module" // Enable ES modules in Node.js } // Or use .mjs extension for ES modules // math.mjs export function add(a, b) { return a + b; } // Import CommonJS in ES modules import { createRequire } from 'module'; const require = createRequire(import.meta.url); const fs = require('fs'); // CommonJS module
Best Practices
javascript// 1. Use named exports for utilities // utils.js export function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } export function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // 2. Use default exports for main functionality // Logger.js class Logger { constructor(level = 'info') { this.level = level; } log(message) { console.log(`[${this.level.toUpperCase()}] ${message}`); } } export default Logger; // 3. Organize related functionality // validation/index.js export function isEmail(email) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } export function isPhone(phone) { return /^\+?[1-9]\d{1,14}$/.test(phone); } export function isRequired(value) { return value != null && value !== ''; } // 4. Use constants module // constants.js export const API_ENDPOINTS = { USERS: '/api/users', POSTS: '/api/posts', COMMENTS: '/api/comments' }; export const HTTP_STATUS = { OK: 200, CREATED: 201, BAD_REQUEST: 400, UNAUTHORIZED: 401, NOT_FOUND: 404, SERVER_ERROR: 500 }; // 5. Environment-specific modules // config/development.js export default { apiUrl: 'http://localhost:3000', debug: true, logLevel: 'debug' }; // config/production.js export default { apiUrl: 'https://api.production.com', debug: false, logLevel: 'error' }; // config/index.js const env = process.env.NODE_ENV || 'development'; const config = await import(`./${env}.js`); export default config.default;