Loading...
Loading...
Powerful patterns built on closure mechanics.
Module 6 · How JavaScript Works
Understanding the engine makes you a better debugger. Execution contexts, the call stack, hoisting, memory allocation, closures, and the prototype chain explain why JavaScript behaves the way it does under the hood.
Learn Advanced Closures in JavaScript — covering Currying, Partial Application, Module Pattern, Memoization with interactive visual examples and step-by-step explanations.
JavaScript looks simple on the surface, but the engine does a lot of work behind the scenes. Variables are hoisted, functions create new execution contexts, the call stack tracks nested calls, and the prototype chain resolves property lookups.
This module is for developers who want to stop guessing and start understanding. Visual diagrams of the call stack, heap, and scope chain make abstract concepts concrete — and directly improve your debugging skills in real projects.
Understanding advanced closures is essential for every JavaScript developer. It shows up in frontend UI code, backend APIs, and framework internals — and getting it wrong leads to bugs that are hard to trace. This chapter builds intuition with visuals so the behavior sticks.
Transform a function with multiple arguments into a chain of single-argument functions.
// Normal function: function add(a, b, c) { return a + b + c; } add(1, 2, 3); // 6 // Curried version: function curriedAdd(a) { return function(b) { return function(c) { return a + b + c; }; }; } curriedAdd(1)(2)(3); // 6
Currying breaks a multi-argument function into nested single-argument functions. Each closure remembers its argument.
Fix some arguments and return a function that takes the rest.
// Create specialized functions from general ones: function log(level, timestamp, message) { console.log(`[${level}] ${timestamp}: ${message}`); } // Partial: fix the level function createLogger(level) { return (message) => { log(level, new Date().toISOString(), message); }; } const error = createLogger("ERROR"); const info = createLogger("INFO"); error("Connection failed"); // [ERROR] 2024-01-15T...: Connection failed info("Server started"); // [INFO] 2024-01-15T...: Server started
// Using .bind() for partial application: function multiply(a, b) { return a * b; } const double = multiply.bind(null, 2); // fix a=2 const triple = multiply.bind(null, 3); // fix a=3 double(5); // 10 triple(5); // 15
Use closures to create private state with a public API.
// The Revealing Module Pattern: const Counter = (() => { // Private state (enclosed) let count = 0; const history = []; // Private function function log(action) { history.push({ action, count, time: Date.now() }); } // Public API (returned) return { increment() { count++; log("inc"); }, decrement() { count--; log("dec"); }, getCount() { return count; }, getHistory() { return [...history]; }, }; })(); Counter.increment(); Counter.getCount(); // 1 Counter.count; // undefined (private!) Counter.history; // undefined (private!)
// Module with private state:
count is private — only accessible through the API
Cache expensive computation results using closures.
function memoize(fn) { const cache = new Map(); // enclosed — persists! return function(...args) { const key = JSON.stringify(args); if (cache.has(key)) { console.log("Cache hit!"); return cache.get(key); } const result = fn(...args); cache.set(key, result); return result; }; } // Usage: const expensiveCalc = memoize((n) => { console.log("Computing..."); return n * n * n; }); expensiveCalc(5); // "Computing..." → 125 expensiveCalc(5); // "Cache hit!" → 125 (instant) expensiveCalc(3); // "Computing..." → 27
// Memoized Fibonacci (exponential → linear): const fib = memoize(function(n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); }); fib(50); // instant! (without memo: would take forever)
Advanced scope interactions and gotchas.
// Closures share the same scope object: function makeActions() { const actions = []; for (var i = 0; i < 3; i++) { actions.push(() => console.log(i)); } return actions; } const acts = makeActions(); acts[0](); // 3 (not 0!) acts[1](); // 3 acts[2](); // 3
All three closures share the same 'i' variable (var is function-scoped). By the time they run, i = 3.
Common questions about advanced closures.