// math.js
export const PI = 3.14;
export default function add(a, b) { return a + b; }
// main.js
import add, { PI } from './math.js';
Topics
37
ArrayBuffer, TypedArrays & Binary Data
Arrays & Array Methods
Async Iterators & Streams
Browser Storage & Web APIs
Classes & Class Syntax
Date, Time & Internationalization (Intl API)
Debugging, Testing & Tooling
Design Patterns in JavaScript
Destructuring, Spread & Rest
DOM & Events
Error Handling
ES Modules
Event Loop & Concurrency
Functional Programming
Iterators & Generators
JSON & Data Serialization
Map, Set, WeakMap & WeakSet
Memory Management & Garbage Collection
Networking: Fetch, XHR, WebSockets & CORS
Numbers, Math & BigInt
Objects, Property Descriptors & Immutability
Optional Chaining & Nullish Coalescing
Package Management, Bundlers & Transpilation (npm, Webpack/Vite, Babel)
Performance Optimization: Debouncing, Throttling & Memoization
Promises & async/await
Prototypes & Inheritance
Proxy & Reflect
Regular Expressions
Scope, Hoisting & Closures
Security: XSS, CSRF & Content Security Policy
Service Workers & Progressive Web Apps
Strings & Template Literals
Symbols & Well-Known Symbols
this & Binding
Types & Coercion
Web Components & Custom Elements
Web Workers & Multithreading
ES Modules
10 questions found
A module can have any number of named exports (imported with matching names in curly braces), but at most one default export (imported with any name you choose, no braces required).
Real-world example
Using a default export for a component's main class/function, and named exports for its helper utilities.
Design Patterns in JavaScript
Use import * as name from '...' to collect all of a module's named exports into a single object, accessed as name.exportedThing.
import * as MathUtils from './math.js';
console.log(MathUtils.PI);
MathUtils.default(2, 3); // calling the default export
Real-world example
Importing an entire utility library under one namespace to avoid dozens of individual named imports.
Functional Programming
ES Modules use static import/export syntax analyzed at parse time (enabling tree-shaking), run in strict mode by default, and support top-level await. CommonJS's require() is dynamic and synchronous, resolved at runtime, and is Node.js's traditional module system.
// ES Module
import { readFile } from 'fs/promises';
// CommonJS
const { readFileSync } = require('fs');
Real-world example
Migrating an older Node.js codebase from require() to import to enable modern bundler optimizations like tree-shaking.
Package Management
Bundlers & Transpilation (npm
Webpack/Vite
Babel)
Tree-shaking is a bundler optimization that removes unused exports from the final bundle. It relies on ES Modules' static, analyzable import/export structure — bundlers can determine at build time exactly what's used, which isn't reliably possible with CommonJS's dynamic require() calls.
// Only 'add' gets bundled if 'subtract' is never imported anywhere
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
Real-world example
Shipping a smaller JS bundle by importing only the specific lodash functions used, instead of the whole library.
Package Management
Bundlers & Transpilation (npm
Webpack/Vite
Babel)
import() is a function-like expression that returns a Promise resolving to the module, and can be called anywhere at runtime — including conditionally — unlike static import which must appear at the top level and is resolved before any code runs.
button.addEventListener('click', async () => {
const { openModal } = await import('./modal.js');
openModal();
});
Real-world example
Lazy-loading a heavy chart library only when the user navigates to a page that actually needs it.
Package Management
Bundlers & Transpilation (npm
Webpack/Vite
Babel)
Why are ES Module imports 'hoisted' and evaluated before the rest of the module's code runs?
IntermediateThe JS engine statically analyzes all import declarations first and resolves/executes the imported modules before running the importing module's own top-level code, ensuring all bindings are available regardless of where the import statement appears in the file.
console.log(PI); // works even though import is written below
import { PI } from './constants.js';
Real-world example
Relying on imported constants or functions being available immediately at the top of a file's execution.
Scope
Hoisting & Closures
ES Module imports are live, read-only views onto the exporting module's variable — if the exporting module later reassigns the exported variable, every importer sees the updated value automatically. CommonJS exports are copied values at the time of require(), so later changes aren't reflected.
// counter.js
export let count = 0;
export function increment() { count++; }
// main.js
import { count, increment } from './counter.js';
increment();
console.log(count); // 1 — reflects the live update
Real-world example
Sharing a mutable, always-current configuration value across multiple modules without a getter function.
Scope
Hoisting & Closures
How does top-level await work, and what constraint does it place on modules that import that module?
AdvancedTop-level await lets a module pause its own evaluation until a Promise resolves, without wrapping code in an async function. Any module that imports a module using top-level await will itself wait for that import to finish resolving before continuing.
// config.js
const response = await fetch('/config.json');
export const config = await response.json();
// main.js — implicitly waits for config.js to finish
import { config } from './config.js';
Real-world example
Loading remote configuration once at module initialization time before the rest of the app starts using it.
Promises & async/await
Import maps let you use bare specifiers (like import 'lodash') directly in the browser without a bundler, by mapping those short names to actual URLs in a <script type='importmap'> block — otherwise browsers require full relative or absolute URLs.
<script type="importmap">
{
"imports": { "lodash": "https://cdn.example.com/lodash.js" }
}
</script>
<script type="module">
import _ from 'lodash';
</script>
Real-world example
Running a bundler-free, browser-native ES Module app while still using clean package-style import names.
Package Management
Bundlers & Transpilation (npm
Webpack/Vite
Babel)
Because of live bindings and evaluation order, one of the two modules will see an export from the other as undefined (or an incomplete value) at the moment its own top-level code runs, since the other module hasn't finished executing yet — the value often becomes correct only later, after both modules finish loading.
// a.js
import { b } from './b.js';
console.log(b); // may log undefined here
export const a = 'A';
// b.js
import { a } from './a.js';
export const b = 'B';
Real-world example
Debugging a mysterious 'undefined is not a function' error caused by two modules that import each other.
Design Patterns in JavaScript