JavaScript
A comprehensive, interactive reference covering every major concept — from primitive types and closures to async/await, DOM APIs, and modern ES2024 features.
§ 01
Variables & Data Types
JavaScript has 8 data types — 7 primitives (stored by value) and 1 complex type, Object (stored by reference). typeof reveals the type at runtime.
// ── DECLARATIONS ────────────────────────────────── var x = 10; // function-scoped, hoisted (legacy) let y = 20; // block-scoped, reassignable const z = 30; // block-scoped, NOT reassignable (prefer this) // ── TYPE COERCION ───────────────────────────────── typeof "hello" // "string" typeof 42 // "number" typeof null // "object" ← famous bug! typeof undefined // "undefined" typeof [] // "object" — use Array.isArray() typeof function(){} // "function" // ── TRUTHY / FALSY ──────────────────────────────── // Falsy: false, 0, "", null, undefined, NaN, 0n // Everything else is truthy if ("") console.log("never"); if ("hello") console.log("runs!"); // ── TYPE CONVERSION ─────────────────────────────── Number("42") // 42 String(99) // "99" Boolean(0) // false parseInt("12.9px") // 12 parseFloat("3.14abc") // 3.14 // ── TEMPLATE LITERALS ──────────────────────────── const name = "World"; const msg = `Hello, ${name}! 2 + 2 = ${2+2}`; // Tagged template const highlight = (strings, ...vals) => strings.reduce((acc, s, i) => acc + s + (`[${vals[i] ?? ''}]`), '');
§ 02
Scope & Hoisting
Scope determines where variables are accessible. JavaScript uses lexical scoping — a function can access variables from where it was defined, not where it is called.
✓ Can read: globalVar, PI, funcVar, funcLet, funcConst
✗ Cannot access: variables from other functions
// ── HOISTING ────────────────────────────────────── // var is hoisted AND initialized to undefined console.log(a); // undefined (not a ReferenceError) var a = 5; // let/const are hoisted but NOT initialized (TDZ) // console.log(b); ← ReferenceError: Cannot access before init let b = 10; // Function declarations are fully hoisted greet(); // Works! "Hello" function greet() { console.log("Hello"); } // Function expressions are NOT hoisted // sayHi(); ← TypeError: sayHi is not a function const sayHi = () => console.log("Hi"); // ── SCOPE EXAMPLE ──────────────────────────────── let count = 0; // global function increment() { count++; // accesses global let local = "I'm local"; // only inside } // console.log(local); ← ReferenceError // ── var LEAKING from blocks ─────────────────────── for (var i = 0; i < 3; i++) {} console.log(i); // 3 — leaked out! for (let j = 0; j < 3; j++) {} // console.log(j); ← ReferenceError — contained
§ 03
Operators
JavaScript's operators go well beyond arithmetic — nullish coalescing, optional chaining, and logical assignment are indispensable modern tools.
// ── EQUALITY ────────────────────────────────────── 5 == "5" // true — coerces types 5 === "5" // false — strict: same type required null == undefined // true (special rule) null === undefined // false // ── NULLISH COALESCING (??) ─────────────────────── const name = null ?? "Guest"; // "Guest" const val = 0 ?? "default"; // 0 — 0 is NOT null/undefined const bad = 0 || "default"; // "default" — 0 is falsy (bug prone!) // ── OPTIONAL CHAINING (?.) ──────────────────────── const user = { profile: { city: "Atlanta" } }; user?.profile?.city // "Atlanta" user?.address?.zip // undefined (no error!) user?.getAge?.() // undefined (not a function, no error) user?.scores?.[0] // undefined // ── LOGICAL ASSIGNMENT ──────────────────────────── let a = null; a ??= "hello"; // a = "hello" (only if null/undefined) let b = 5; b &&= 10; // b = 10 (only if b is truthy) let c = 0; c ||= 99; // c = 99 (only if c is falsy) // ── SPREAD / REST ───────────────────────────────── const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; // [1,2,3,4,5] const obj2 = { ...user, age: 30 }; // shallow copy + extend function sum(...nums) { // rest: gathers remaining return nums.reduce((a, b) => a + b, 0); } sum(1, 2, 3, 4); // 10
§ 04
Control Flow
Conditionals, loops, and branching. switch with fall-through, labeled breaks, and the compact ternary/logical patterns used in real code.
// Ternary const grade = score >= 90 ? "A" : score >= 80 ? "B" : "C"; // Switch with fall-through switch (day) { case "Sat": case "Sun": relax(); break; // both hit this default: work(); } // Short-circuit evaluation isLoggedIn && showDashboard(); user || redirectToLogin();
// for..of — iterates values for (const item of [1,2,3]) { console.log(item); } // for..in — iterates keys for (const key in { a:1, b:2 }) { console.log(key); // "a", "b" } // while / do-while let n = 3; while (n > 0) n--; do { prompt(); } while (!valid); // runs at least once // break / continue / labels outer: for (...) { for (...) { break outer; // exits outer loop } }
§ 05
Functions
First-class citizens in JS — passed as arguments, returned from other functions, stored in variables. Arrow functions, IIFE, recursion, currying, memoization.
// ── 4 WAYS TO WRITE A FUNCTION ──────────────────── function declaration(x) { return x * 2; } // hoisted const expression = function(x) { return x * 2; }; const arrow = (x) => x * 2; // no own 'this' const obj = { method(x) { return x * 2; } // shorthand method }; // ── DEFAULT PARAMETERS ──────────────────────────── function greet(name = "World", greeting = "Hello") { return `${greeting}, ${name}!`; } // ── HIGHER-ORDER FUNCTIONS ──────────────────────── const double = x => x * 2; const applyTwice = (fn, x) => fn(fn(x)); // fn as argument applyTwice(double, 3); // 12 // ── CURRYING ───────────────────────────────────── const add = a => b => a + b; const add5 = add(5); // partial application add5(3); // 8 // ── MEMOIZATION ────────────────────────────────── function memoize(fn) { const cache = new Map(); return (...args) => { const key = JSON.stringify(args); if (cache.has(key)) return cache.get(key); const result = fn(...args); cache.set(key, result); return result; }; } // ── IIFE ───────────────────────────────────────── const result = (function() { return "immediately invoked!"; })();
§ 06
Array Methods
JavaScript arrays have over 30 built-in methods. The functional trio — map, filter, reduce — forms the backbone of modern data transformation.
const nums = [1, 2, 3, 4, 5, 6, 7, 8]; // ── TRANSFORM ───────────────────────────────────── nums.map(x => x ** 2) // [1,4,9,16,25,36,49,64] nums.filter(x => x % 2 === 0) // [2,4,6,8] nums.reduce((acc, x) => acc + x, 0) // 36 // ── SEARCH ──────────────────────────────────────── nums.find(x => x > 5) // 6 nums.findIndex(x => x > 5) // 5 nums.some(x => x > 7) // true nums.every(x => x > 0) // true nums.includes(4) // true // ── MUTATE ──────────────────────────────────────── nums.push(9); // add to end nums.pop(); // remove from end nums.unshift(0); // add to start nums.shift(); // remove from start nums.splice(2, 1, 99); // remove 1 at idx 2, insert 99 nums.reverse(); // reverses in place! nums.sort((a, b) => a - b); // numeric sort // ── NON-MUTATING ───────────────────────────────── nums.slice(1, 4); // [2,3,4] — no mutation [...new Set(nums)]; // deduplicate nums.flat(Infinity); // flatten nested arrays nums.flatMap(x => [x, x*2]); // map + flatten 1 level // ── CHAINING ───────────────────────────────────── [1,2,3,4,5,6] .filter(x => x % 2 === 0) // [2,4,6] .map(x => x ** 2) // [4,16,36] .reduce((a, b) => a + b); // 56 // ── ARRAY.FROM / FILL / OF ──────────────────────── Array.from({length:5}, (_, i) => i+1); // [1,2,3,4,5] new Array(5).fill(0); // [0,0,0,0,0]
§ 07
Objects & Methods
Objects are the heart of JavaScript. Understand property descriptors, getters/setters, Object utility methods, and the this keyword.
// ── OBJECT CREATION ─────────────────────────────── const user = { name: "Alice", age: 30, greet() { return `Hi, I'm ${this.name}`; }, // method get info() { return `${this.name}, ${this.age}`; }, // getter set info(v) { [this.name, this.age] = v.split(","); } // setter }; // ── COMPUTED / DYNAMIC KEYS ─────────────────────── const key = "score"; const data = { [key]: 100, [`${key}_max`]: 200 }; // ── Object METHODS ──────────────────────────────── Object.keys(user) // ["name","age"] Object.values(user) // ["Alice", 30] Object.entries(user) // [["name","Alice"],["age",30]] Object.fromEntries(entries) // reverse of entries Object.assign({}, user, { age: 31 }) // shallow merge Object.freeze(user) // no more changes // ── DEEP CLONE ──────────────────────────────────── const deep = structuredClone(user); // ES2022 ✓ const old = JSON.parse(JSON.stringify(user)); // classic // ── THIS KEYWORD ───────────────────────────────── const obj = { val: 42, getVal() { return this.val; } }; const bound = obj.getVal.bind(obj); // lock 'this' obj.getVal.call({ val: 99 }); // temporary this obj.getVal.apply(obj, []); // same but array args
§ 08
Map & Set
Map allows any type as key (not just strings). Set stores unique values. Both are iterable and maintain insertion order.
| Key (any type) | Value |
|---|---|
| "name" | "Alice" |
| 42 (number) | "answer" |
| true (bool) | [1,2,3] |
| {id:1} (object) | "user obj key!" |
| Values (duplicates removed) | Has? |
|---|---|
| 1, 2, 3 | ✓ deduplicated |
| "apple", "banana" | ✓ unique strings |
| NaN | ✓ only once |
| {} vs {} | ✗ different refs |
§ 09
Destructuring
Unpack arrays and objects into variables in a single expression. The most used ES6 feature in real codebases.
const [a, b, c] = [1, 2, 3]; const [x, , z] = [1, 2, 3]; // skip middle const [head, ...tail] = [1,2,3,4]; // head=1, tail=[2,3,4] // Swap variables — no temp needed! let p = 1, q = 2; [p, q] = [q, p]; // p=2, q=1 // Default values const [m = 10, n = 20] = [99]; // m=99, n=20 // From function return function range() { return [1, 100]; } const [min, max] = range();
const user = { name:"Bob", age:25, city:"LA" }; // Basic const { name, age } = user; // Rename const { name: userName } = user; // Default values const { role = "user" } = user; // Rest — collect remainder const { city, ...rest } = user; // rest = { name:"Bob", age:25 } // Nested const { address: { zip } } = { address: { zip: "30024" } }; // Function parameters function display({ name, age = 0 }) { return `${name} is ${age}`; }
§ 10
Closures
A closure is a function that remembers the variables from its outer scope even after that scope has finished executing.
count variable is private, shared only through the returned API.
// ── BASIC CLOSURE ───────────────────────────────── function makeCounter() { let count = 0; // private state return { increment: () => ++count, decrement: () => --count, reset: () => count = 0, value: () => count }; } const counter = makeCounter(); counter.increment(); // 1 counter.increment(); // 2 counter.value(); // 2 // ── CLOSURE FOR MODULE PATTERN ──────────────────── const wallet = (function() { let balance = 0; return { deposit: n => balance += n, withdraw: n => { if (n > balance) throw new Error("Insufficient funds"); balance -= n; }, getBalance: () => balance }; })(); // ── CLOSURE PITFALL (var in loops) ──────────────── // Bug: all callbacks see the same 'i' (after loop ends) for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // prints 3,3,3 } // Fix: use let (block-scoped per iteration) for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // prints 0,1,2 ✓ }
§ 11
Classes & OOP
ES6 classes are syntactic sugar over prototype chains. Private fields (#), static methods, inheritance, and mixins give you full OOP power.
class Animal { #name; // private field (ES2022) static count = 0; // static class field constructor(name) { this.#name = name; Animal.count++; } speak() { return `${this.#name} makes a noise`; } get name() { return this.#name; } set name(v) { this.#name = v; } static create(n) { return new Animal(n); } } class Dog extends Animal { constructor(name, breed) { super(name); // MUST call super first this.breed = breed; } speak() { return `${super.speak()} — Woof!`; } fetch(item) { return `${this.name} fetches ${item}`; } } // Mixin — share behavior without inheritance const Serializable = (Base) => class extends Base { serialize() { return JSON.stringify(this); } static deserialize(s) { return JSON.parse(s); } }; class SerializableDog extends Serializable(Dog) {}
§ 12
The Prototype Chain
Every JavaScript object has a hidden [[Prototype]] link. Property lookups traverse this chain until null is reached.
// Object.create — explicit prototype const animal = { describe() { return `I am ${this.name}`; } }; const dog = Object.create(animal); dog.name = "Rex"; dog.describe(); // "I am Rex" — found on prototype // Prototype chain lookup order // dog → animal → Object.prototype → null // Check prototype membership dog instanceof Object // true Object.getPrototypeOf(dog) === animal // true // hasOwnProperty vs inherited dog.hasOwnProperty("name") // true — own dog.hasOwnProperty("describe") // false — on prototype // Prototype augmentation (use sparingly!) Array.prototype.sum = function() { return this.reduce((a, b) => a + b, 0); }; [1, 2, 3].sum(); // 6
§ 13
Iterators & Generators
The iterator protocol powers for...of, spread, and destructuring. Generators are pausable functions that produce sequences on demand.
// ── GENERATOR FUNCTION ──────────────────────────── function* range(start, end) { for (let i = start; i <= end; i++) { yield i; // pauses, returns i } } for (const n of range(1, 5)) { console.log(n); // 1,2,3,4,5 } // ── INFINITE GENERATOR ─────────────────────────── function* ids() { let id = 1; while (true) yield id++; } const gen = ids(); gen.next().value; // 1 gen.next().value; // 2 gen.next().value; // 3 — can go forever // ── CUSTOM ITERABLE ─────────────────────────────── const range2 = { [Symbol.iterator]() { let i = 0; return { next: () => i < 5 ? { value: i++, done: false } : { value: undefined, done: true } }; } }; [...range2]; // [0,1,2,3,4] // ── yield* DELEGATION ──────────────────────────── function* concat(...iters) { for (const it of iters) yield* it; }
§ 14
Promises & Async/Await
Asynchronous code that reads like synchronous. Understand the event loop, microtask queue, and how async/await desugars to Promise chains.
// ── PROMISE ─────────────────────────────────────── const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // Chaining fetchUser(1) .then(user => fetchPosts(user.id)) .then(posts => render(posts)) .catch(err => console.error(err)) .finally(() => hideSpinner()); // ── ASYNC / AWAIT ───────────────────────────────── async function loadData() { try { const user = await fetchUser(1); // pauses here const posts = await fetchPosts(user.id); return posts; } catch (err) { console.error("Failed:", err.message); } } // ── PARALLEL REQUESTS ───────────────────────────── // Sequential (slow): each waits for prev const a = await fetch1(); const b = await fetch2(); // Parallel (fast): both fire simultaneously const [a, b] = await Promise.all([fetch1(), fetch2()]); // ── PROMISE UTILITIES ───────────────────────────── Promise.all([p1, p2, p3]) // all must resolve Promise.allSettled([p1, p2]) // waits for all, captures rejections Promise.race([p1, p2]) // first to settle wins Promise.any([p1, p2]) // first to RESOLVE wins (ignores rejects) Promise.resolve(42) // instantly resolved
§ 15
Fetch & APIs
The Fetch API is the modern way to make HTTP requests. Combine with async/await for clean, readable network code.
// ── GET ─────────────────────────────────────────── async function getUser(id) { const res = await fetch(`https://api.example.com/users/${id}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); // parses JSON body } // ── POST ────────────────────────────────────────── async function createPost(data) { const res = await fetch("/api/posts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); return res.json(); } // ── ABORT CONTROLLER ───────────────────────────── const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 5000); await fetch(url, { signal: controller.signal }); clearTimeout(timer); // ── STREAMING RESPONSE ──────────────────────────── const res = await fetch(url); const reader = res.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; processChunk(value); }
§ 16
DOM Manipulation
Create, read, update, and delete elements in the page. The DOM is a live tree — every change immediately reflects in the browser.
// ── SELECTING ───────────────────────────────────── document.querySelector('.card') // first match document.querySelectorAll('li') // NodeList document.getElementById('main') // by id document.getElementsByClassName('btn') // HTMLCollection el.closest('.container') // walk UP the tree el.matches(':hover') // test selector // ── CREATING ────────────────────────────────────── const div = document.createElement('div'); div.textContent = 'New Element'; div.className = 'card amber'; div.setAttribute('data-id', '42'); div.dataset.id = '42'; // same via dataset parent.append(div); // add at end parent.prepend(div); // add at start parent.insertBefore(div, sibling); // insert before div.after(other); // after this element // ── STYLING ─────────────────────────────────────── el.style.color = 'red'; el.style.cssText = 'color:red; font-size:16px'; el.classList.add('active'); el.classList.remove('hidden'); el.classList.toggle('open'); // add if absent el.classList.replace('old', 'new'); el.classList.contains('active'); // boolean el.style.setProperty('--color', 'red'); // CSS vars! // ── SAFE HTML ───────────────────────────────────── el.textContent = '<b>safe</b>'; // escaped, safe el.innerHTML = '<b>rendered</b>'; // ⚠ XSS risk! el.insertAdjacentHTML('beforeend', html); // efficient insert
§ 17
Events & Delegation
Events bubble from target to root. Event delegation attaches one listener to a parent, handling clicks on all children — even future ones.
// ── ADD / REMOVE LISTENERS ──────────────────────── const handler = (e) => console.log(e.target); el.addEventListener('click', handler); el.removeEventListener('click', handler); // must be same reference! // Once option el.addEventListener('click', handler, { once: true }); // ── EVENT OBJECT ───────────────────────────────── el.addEventListener('click', (e) => { e.target // element clicked e.currentTarget // element with listener e.type // "click" e.preventDefault() // stop default (link, form submit) e.stopPropagation()// stop bubbling e.clientX, e.clientY // mouse position e.key, e.code // keyboard }); // ── EVENT DELEGATION ───────────────────────────── // One listener handles ALL current and future children document.querySelector('#list') .addEventListener('click', (e) => { if (e.target.matches('li')) { handleItem(e.target.dataset.id); } }); // ── CUSTOM EVENTS ──────────────────────────────── const evt = new CustomEvent('userLogin', { detail: { user: "Alice" }, bubbles: true }); document.dispatchEvent(evt); // ── INTERSECTION OBSERVER ───────────────────────── const obs = new IntersectionObserver((entries) => { entries.forEach(e => e.target.classList.toggle('visible', e.isIntersecting)); }, { threshold: 0.2 }); obs.observe(el);
§ 18
Web Storage
localStorage persists after closing the browser. sessionStorage clears when the tab closes. Both are synchronous key-value stores limited to ~5MB of strings.
// ── localStorage (persists) ──────────────────────── localStorage.setItem('key', 'value'); localStorage.getItem('key'); // "value" localStorage.removeItem('key'); localStorage.clear(); // ⚠ Can only store strings! Serialize objects: localStorage.setItem('user', JSON.stringify({ name:"Alice" })); const user = JSON.parse(localStorage.getItem('user')); // ── sessionStorage (tab only) ────────────────────── sessionStorage.setItem('token', 'abc123'); // ── Iterate all keys ────────────────────────────── for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); console.log(key, localStorage.getItem(key)); } // ── storage event (cross-tab communication) ─────── window.addEventListener('storage', (e) => { console.log(e.key, e.oldValue, e.newValue); }); // ── IndexedDB (structured, large data) ──────────── // For storing > 5MB or complex objects, use IndexedDB // or libraries like idb / Dexie.js // ── Cookie vs localStorage ──────────────────────── // Cookies: sent with every HTTP request, expire, small // localStorage: client-only, no expiry, ~5MB // sessionStorage: cleared on tab close, ~5MB
§ 19
Regular Expressions
A mini-language for pattern matching. Use .test() for boolean checks, .match()/.exec() for extraction, .replace() for transformation.
// ── CREATING ────────────────────────────────────── const re1 = /\d+/g; // literal const re2 = new RegExp('\\d+', 'g'); // dynamic // ── METHODS ─────────────────────────────────────── /hello/.test("hello world") // true "12px".match(/\d+/) // ["12"] "a1b2".matchAll(/[a-z](\d)/g) // iterator of all matches+groups "hello".replace(/l/g, 'r') // "herro" "a,b,,c".split(/,+/) // ["a","b","c"] /\d+/.exec("abc123") // match object // ── USEFUL PATTERNS ────────────────────────────── /^[\w.-]+@[\w.-]+\.\w{2,}$/ // email /^\+?[\d\s\-()]{7,15}$/ // phone /^https?:\/\/[^\s]+$/ // url /#[0-9A-Fa-f]{3,6}\b/g // hex color /\b\d{4}-\d{2}-\d{2}\b/ // date YYYY-MM-DD /(?<=\$)\d+(\.\d{2})?/g // price after $ // ── GROUPS ──────────────────────────────────────── const m = "2025-01-15".match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/); m.groups.y // "2025" — named group // ── replaceAll with function ────────────────────── "a1 b2 c3".replace(/(\w)(\d)/g, (match, letter, num) => `${letter.toUpperCase()}${Number(num)*10}` ); // "A10 B20 C30"
§ 20
Modern ES6+
A tour of every major feature added since ES6 (2015) through ES2024 — the features you'll see in every modern codebase.
// ── PROXY (ES6) ─────────────────────────────────── const handler = { get(target, prop) { return prop in target ? target[prop] : `${prop} not found`; }, set(target, prop, val) { if (typeof val !== "number") throw TypeError("Must be number"); target[prop] = val; return true; } }; const p = new Proxy({}, handler); // ── SYMBOL (ES6) ───────────────────────────────── const id = Symbol('id'); const obj = { [id]: 42 }; // non-enumerable key Symbol.for('shared') // global registry // ── Array.at() (ES2022) ─────────────────────────── const arr = [1,2,3,4,5]; arr.at(-1) // 5 — last element arr.at(-2) // 4 // ── Object.hasOwn (ES2022) ──────────────────────── Object.hasOwn(obj, 'name') // better than hasOwnProperty // ── structuredClone (ES2022) ────────────────────── const deep = structuredClone({ a: { b: 1 } }); // ── Array.from { groupBy } (ES2024) ─────────────── const items = [ { type:'fruit', name:'apple' }, { type:'veg', name:'carrot' }, { type:'fruit', name:'banana' } ]; Object.groupBy(items, item => item.type); // { fruit:[apple,banana], veg:[carrot] } // ── Temporal (ES2024) — better dates ────────────── const now = Temporal.Now.plainDateTimeISO(); now.add({ days: 30 }); // ── Logical Assignment (ES2021) ─────────────────── x ??= "default"; // x = x ?? "default" x ||= 0; // x = x || 0 x &&= 1; // x = x && 1
§ 21
Error Handling
Robust error handling separates production code from tutorials. Custom error types, error boundaries, and safe async patterns.
// ── TRY / CATCH / FINALLY ───────────────────────── try { const data = JSON.parse(invalidJSON); } catch (e) { if (e instanceof SyntaxError) { console.error("Bad JSON:", e.message); } else throw e; // re-throw unknown errors } finally { closeConnection(); // always runs } // ── CUSTOM ERROR CLASSES ────────────────────────── class ValidationError extends Error { constructor(field, message) { super(message); this.name = 'ValidationError'; this.field = field; } } class NetworkError extends Error { constructor(status, url) { super(`HTTP ${status}`); this.name = 'NetworkError'; this.status = status; this.url = url; } } // ── ASYNC ERROR PATTERNS ───────────────────────── async function safeFetch(url) { const res = await fetch(url); if (!res.ok) throw new NetworkError(res.status, url); return res.json(); } // ── Result pattern (no throws) ──────────────────── async function safeRun(fn) { try { return [null, await fn()]; } catch (e) { return [e, null]; } } const [err, data] = await safeRun(() => fetchData()); if (err) handleError(err);
§ 22
Design Patterns
Proven solutions to recurring problems. These patterns appear constantly in real JavaScript codebases.
// ── SINGLETON ───────────────────────────────────── const Store = (function() { let instance; return { getInstance() { if (!instance) instance = { state: {}, version: 1 }; return instance; } }; })(); // ── OBSERVER / PubSub ───────────────────────────── class EventBus { #listeners = {}; on(event, cb) { (this.#listeners[event] ??= []).push(cb); } off(event, cb) { this.#listeners[event] = (this.#listeners[event] ?? []).filter(l => l !== cb); } emit(event, data) { (this.#listeners[event] ?? []).forEach(cb => cb(data)); } } // ── FUNCTION COMPOSITION ───────────────────────── const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x); const process = pipe( x => x.trim(), x => x.toLowerCase(), x => x.replace(/\s+/g, '-') ); process(" Hello World "); // "hello-world" // ── DECORATOR PATTERN ───────────────────────────── function withLogging(fn) { return function(...args) { console.log(`Calling ${fn.name} with`, args); const result = fn.apply(this, args); console.log(`Result:`, result); return result; }; } const loggedAdd = withLogging((a, b) => a + b);