javascript
examples
examples.js⚡javascript
/**
* ============================================================
* 22.4 ES MODULES INTERNALS - EXAMPLES
* ============================================================
*
* Note: Some examples require running as ES modules (.mjs)
* or with "type": "module" in package.json
*/
/**
* ============================================================
* EXAMPLE 1: LIVE BINDINGS
* ============================================================
*
* This demonstrates that ES Module imports are live bindings,
* not copies of values.
*/
console.log("=== Example 1: Live Bindings Concept ===\n");
// Simulate module behavior with closures
const createCounterModule = () => {
let count = 0;
return {
// Live binding - getter returns current value
get count() { return count; },
increment() { count++; }
};
};
const counter = createCounterModule();
console.log("Initial count:", counter.count); // 0
counter.increment();
console.log("After increment:", counter.count); // 1
counter.increment();
console.log("After another increment:", counter.count); // 2
console.log("\nThis is how ES Module live bindings work:");
console.log("- Exports are like getters, not value copies");
console.log("- Importers always see the current value\n");
/**
* ============================================================
* EXAMPLE 2: MODULE SINGLETON PATTERN
* ============================================================
*/
console.log("=== Example 2: Module Singleton Pattern ===\n");
// Each module is only evaluated once
// This creates a natural singleton pattern
class ModuleCache {
constructor() {
this.cache = new Map();
this.loadCount = new Map();
}
// Simulate module loading
load(path, factory) {
const count = (this.loadCount.get(path) || 0) + 1;
this.loadCount.set(path, count);
console.log(` Loading '${path}' (attempt #${count})`);
if (this.cache.has(path)) {
console.log(` → Returning cached module`);
return this.cache.get(path);
}
console.log(` → Executing module factory`);
const moduleExports = factory();
this.cache.set(path, moduleExports);
return moduleExports;
}
}
const moduleCache = new ModuleCache();
// Module factory - would only run once in real ES modules
const createConfigModule = () => {
console.log(" → Config module: Initializing...");
return {
apiUrl: 'https://api.example.com',
debug: true
};
};
console.log("First import of config:");
const config1 = moduleCache.load('./config.js', createConfigModule);
console.log("\nSecond import of config:");
const config2 = moduleCache.load('./config.js', createConfigModule);
console.log("\nThird import of config:");
const config3 = moduleCache.load('./config.js', createConfigModule);
console.log("\nAll references point to same object:", config1 === config2 && config2 === config3);
console.log();
/**
* ============================================================
* EXAMPLE 3: CIRCULAR DEPENDENCY SIMULATION
* ============================================================
*/
console.log("=== Example 3: Circular Dependencies ===\n");
// Simulate how ES Modules handle circular dependencies
class ModuleSystem {
constructor() {
this.modules = new Map();
this.executing = new Set();
}
define(name, dependencies, factory) {
this.modules.set(name, { dependencies, factory, exports: null });
}
require(name) {
const module = this.modules.get(name);
if (!module) {
throw new Error(`Module '${name}' not found`);
}
// If already executed, return exports
if (module.exports !== null) {
console.log(` [${name}] Already loaded, returning exports`);
return module.exports;
}
// If currently executing (circular!), return partial exports
if (this.executing.has(name)) {
console.log(` [${name}] ⚠️ Circular! Returning partial exports`);
// Create empty exports object that will be filled later
module.exports = {};
return module.exports;
}
// Start executing
this.executing.add(name);
console.log(` [${name}] Executing...`);
// Load dependencies
const deps = module.dependencies.map(dep => this.require(dep));
// Create exports object
module.exports = {};
// Execute factory with dependencies and exports
module.factory(module.exports, ...deps);
this.executing.delete(name);
console.log(` [${name}] Done!`);
return module.exports;
}
}
const system = new ModuleSystem();
// Define modules with circular dependency
system.define('a', ['b'], (exports, b) => {
console.log(` [a] Setting up, b.value = ${b.value}`);
exports.value = 'A';
exports.getValue = () => exports.value;
});
system.define('b', ['a'], (exports, a) => {
console.log(` [b] Setting up, a.value = ${a.value}`); // undefined!
exports.value = 'B';
exports.getValue = () => exports.value;
exports.getA = () => a.value; // Will work later!
});
console.log("Loading module 'a' (which requires 'b', which requires 'a'):\n");
const moduleA = system.require('a');
console.log("\nAfter loading:");
console.log("a.value:", moduleA.value);
console.log("a.getValue():", moduleA.getValue());
const moduleB = system.require('b');
console.log("b.value:", moduleB.value);
console.log("b.getA() (deferred access):", moduleB.getA());
console.log();
/**
* ============================================================
* EXAMPLE 4: DYNAMIC IMPORT SIMULATION
* ============================================================
*/
console.log("=== Example 4: Dynamic Import Patterns ===\n");
// Simulate dynamic import behavior
async function simulateDynamicImport(path) {
console.log(` Dynamic import: '${path}'`);
// Simulate async loading
await new Promise(resolve => setTimeout(resolve, 100));
// Simulate module content based on path
const modules = {
'./heavy-feature.js': {
default: class HeavyFeature {
run() { return 'Heavy feature running!'; }
},
helper: () => 'Helper function'
},
'./locale-en.js': {
greeting: 'Hello',
farewell: 'Goodbye'
},
'./locale-es.js': {
greeting: 'Hola',
farewell: 'Adiós'
}
};
if (!modules[path]) {
throw new Error(`Module '${path}' not found`);
}
return modules[path];
}
// Pattern 1: Code Splitting
async function loadHeavyFeature() {
console.log("Pattern 1: Code Splitting");
const module = await simulateDynamicImport('./heavy-feature.js');
const Feature = module.default;
const feature = new Feature();
console.log(` Result: ${feature.run()}\n`);
}
// Pattern 2: Conditional Loading
async function loadLocale(lang) {
console.log(`Pattern 2: Conditional Loading (lang: ${lang})`);
const localePath = `./locale-${lang}.js`;
const locale = await simulateDynamicImport(localePath);
console.log(` Greeting: ${locale.greeting}`);
console.log(` Farewell: ${locale.farewell}\n`);
}
// Pattern 3: Lazy Loading
function createLazyButton(modulePath) {
console.log("Pattern 3: Lazy Loading (deferred until click)");
let handler = null;
return {
async click() {
if (!handler) {
console.log(" First click - loading module...");
const module = await simulateDynamicImport(modulePath);
handler = module.helper;
}
console.log(` Handler result: ${handler()}`);
}
};
}
async function runDynamicImportExamples() {
await loadHeavyFeature();
await loadLocale('en');
await loadLocale('es');
const button = createLazyButton('./heavy-feature.js');
console.log("Created button (module not loaded yet)");
await button.click(); // Loads module
await button.click(); // Uses cached module
console.log();
}
runDynamicImportExamples().then(() => {
/**
* ============================================================
* EXAMPLE 5: MODULE RESOLUTION
* ============================================================
*/
console.log("=== Example 5: Module Resolution ===\n");
// Simulate how module specifiers are resolved
function resolveModuleSpecifier(specifier, importerPath) {
console.log(` Resolving '${specifier}' from '${importerPath}'`);
// Bare specifier (package name)
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
console.log(` → Bare specifier: Looking in node_modules`);
return `/node_modules/${specifier}/index.js`;
}
// Absolute path
if (specifier.startsWith('/')) {
console.log(` → Absolute path`);
return specifier;
}
// Relative path
const importerDir = importerPath.substring(0, importerPath.lastIndexOf('/'));
if (specifier.startsWith('./')) {
const resolved = importerDir + specifier.substring(1);
console.log(` → Relative path: ${resolved}`);
return resolved;
}
if (specifier.startsWith('../')) {
const parentDir = importerDir.substring(0, importerDir.lastIndexOf('/'));
const resolved = parentDir + specifier.substring(2);
console.log(` → Parent relative: ${resolved}`);
return resolved;
}
return specifier;
}
const currentFile = '/app/src/components/Button.js';
console.log("Examples of module resolution:\n");
resolveModuleSpecifier('react', currentFile);
resolveModuleSpecifier('./utils', currentFile);
resolveModuleSpecifier('../shared/helpers', currentFile);
resolveModuleSpecifier('/lib/vendor.js', currentFile);
console.log();
/**
* ============================================================
* EXAMPLE 6: NAMED VS DEFAULT EXPORTS
* ============================================================
*/
console.log("=== Example 6: Named vs Default Exports ===\n");
// Simulate different export patterns
const moduleWithNamedExports = {
__esModule: true,
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b
};
const moduleWithDefaultExport = {
__esModule: true,
default: class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
};
const moduleWithBoth = {
__esModule: true,
default: (a, b) => a + b,
PI: 3.14159,
E: 2.71828
};
console.log("Named exports - better for tree-shaking:");
console.log(` import { add, subtract } from 'math'`);
console.log(` // Only add and subtract bundled if only they're used\n`);
console.log("Default export - single main thing:");
console.log(` import Calculator from 'calculator'`);
console.log(` // Entire class bundled\n`);
console.log("Mixed - flexibility:");
console.log(` import add, { PI, E } from 'math'`);
console.log(` // Default + named exports\n`);
// Using the modules
console.log("Using named exports:", moduleWithNamedExports.add(2, 3));
const Calc = moduleWithDefaultExport.default;
console.log("Using default export:", new Calc().add(2, 3));
console.log("Using mixed:", moduleWithBoth.default(2, 3), moduleWithBoth.PI);
console.log();
/**
* ============================================================
* EXAMPLE 7: RE-EXPORTS (BARREL FILES)
* ============================================================
*/
console.log("=== Example 7: Re-exports (Barrel Files) ===\n");
// Simulate a barrel file pattern
// Individual modules
const button = { Button: 'Button Component' };
const input = { Input: 'Input Component', TextArea: 'TextArea Component' };
const modal = { Modal: 'Modal Component', ModalHeader: 'ModalHeader' };
// Barrel file (index.js) that re-exports everything
const componentsBarrel = {
// Re-export all from button
...button,
// Re-export specific from input
Input: input.Input,
TextArea: input.TextArea,
// Re-export with rename
Dialog: modal.Modal,
DialogHeader: modal.ModalHeader
};
console.log("Barrel file allows importing from single location:");
console.log(` import { Button, Input, Dialog } from './components'`);
console.log("\nInstead of:");
console.log(` import { Button } from './components/button'`);
console.log(` import { Input } from './components/input'`);
console.log(` import { Modal as Dialog } from './components/modal'\n`);
console.log("Barrel exports:", Object.keys(componentsBarrel));
console.log();
/**
* ============================================================
* EXAMPLE 8: MODULE NAMESPACE OBJECT
* ============================================================
*/
console.log("=== Example 8: Module Namespace Object ===\n");
// Simulate import * as namespace
const mathModule = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
PI: 3.14159,
E: 2.71828
};
// Simulating: import * as math from './math.js'
const math = Object.freeze(
Object.assign(
Object.create(null),
mathModule,
{ [Symbol.toStringTag]: 'Module' }
)
);
console.log("Module namespace object:");
console.log(" math:", math);
console.log(" typeof math:", typeof math);
console.log(" Object.keys(math):", Object.keys(math));
console.log(" math[Symbol.toStringTag]:", math[Symbol.toStringTag]);
console.log(" Object.isFrozen(math):", Object.isFrozen(math));
// Try to modify (fails silently or throws in strict mode)
try {
math.add = null; // Won't work
} catch (e) {
console.log(" Cannot modify namespace:", e.message);
}
console.log(" math.add still works:", math.add(1, 2));
console.log();
/**
* ============================================================
* EXAMPLE 9: IMPORT ASSERTIONS
* ============================================================
*/
console.log("=== Example 9: Import Assertions (Future) ===\n");
console.log("Import assertions allow specifying module type:\n");
console.log("JSON imports:");
console.log(` import config from './config.json' assert { type: 'json' };`);
console.log(` // config is parsed JSON object\n`);
console.log("CSS imports (proposal):");
console.log(` import styles from './styles.css' assert { type: 'css' };`);
console.log(` // styles is CSSStyleSheet object\n`);
// Simulate JSON import behavior
const jsonModuleContent = '{"name": "MyApp", "version": "1.0.0"}';
const parsedConfig = JSON.parse(jsonModuleContent);
console.log("Simulated JSON import:", parsedConfig);
console.log();
/**
* ============================================================
* EXAMPLE 10: TOP-LEVEL AWAIT SIMULATION
* ============================================================
*/
console.log("=== Example 10: Top-Level Await ===\n");
// Simulate how TLA affects module loading
async function simulateModuleWithTLA(name, asyncOperation) {
console.log(` [${name}] Starting evaluation...`);
const result = await asyncOperation();
console.log(` [${name}] Async operation complete: ${result}`);
console.log(` [${name}] Module fully loaded`);
return { value: result };
}
async function simulateModuleLoading() {
console.log("Loading module with top-level await:\n");
// This simulates: export const data = await fetchData();
const moduleWithTLA = await simulateModuleWithTLA(
'data-module',
() => new Promise(resolve => {
setTimeout(() => resolve('Fetched data!'), 500);
})
);
console.log("\nImporting module waits for TLA to complete.");
console.log("Module export is ready:", moduleWithTLA.value);
}
await simulateModuleLoading();
console.log();
/**
* ============================================================
* EXAMPLE 11: IMPORT.META
* ============================================================
*/
console.log("=== Example 11: import.meta ===\n");
// Simulate import.meta object
const importMeta = {
url: 'file:///home/user/project/src/module.js',
resolve: (specifier) => {
// Simulate import.meta.resolve()
if (specifier.startsWith('.')) {
return new URL(specifier, importMeta.url).href;
}
return specifier;
}
};
console.log("import.meta provides module metadata:\n");
console.log("import.meta.url:", importMeta.url);
console.log("\nUseful for:");
console.log(" - Getting current module's path");
console.log(" - Loading relative resources");
console.log(" - Constructing URLs relative to module\n");
// Common patterns
console.log("Common patterns:");
console.log(` const __filename = new URL(import.meta.url).pathname`);
console.log(` const __dirname = new URL('.', import.meta.url).pathname`);
const filename = new URL(importMeta.url).pathname;
const dirname = new URL('.', importMeta.url).pathname;
console.log(`\n __filename: ${filename}`);
console.log(` __dirname: ${dirname}`);
console.log();
/**
* ============================================================
* EXAMPLE 12: TREE-SHAKING SIMULATION
* ============================================================
*/
console.log("=== Example 12: Tree-Shaking ===\n");
// Demonstrate why ES Modules enable tree-shaking
const fullLibrary = {
usedFunction: () => 'I am used!',
unusedFunction: () => 'I am never called',
anotherUnused: () => 'Also never called',
CONSTANT: 42
};
// Static analysis can determine:
// - Which exports are imported
// - Which of those are actually used
function analyzeImports(sourceCode) {
// Simplified analysis
const imports = [];
const importMatch = sourceCode.match(/import\s*{([^}]+)}/);
if (importMatch) {
imports.push(...importMatch[1].split(',').map(s => s.trim()));
}
return imports;
}
function analyzeUsage(sourceCode, imports) {
const used = [];
imports.forEach(imp => {
// Very simplified - just check if name appears in code
if (sourceCode.includes(imp + '(') || sourceCode.includes(imp + ';')) {
used.push(imp);
}
});
return used;
}
const code = `
import { usedFunction, unusedFunction, CONSTANT } from './lib.js';
console.log(usedFunction());
console.log(CONSTANT);
`;
const imports = analyzeImports(code);
const used = analyzeUsage(code, imports);
console.log("Source code analysis:");
console.log(" Imported:", imports);
console.log(" Actually used:", used);
console.log("\nTree-shaking would remove:",
imports.filter(i => !used.includes(i)));
console.log("\nFinal bundle only includes used exports!");
console.log();
console.log("=== Examples Complete ===\n");
});