Closures & Lexical Scope
📖 Concept
A closure is created when a function "remembers" variables from its outer (enclosing) scope, even after the outer function has finished executing.
Lexical Scope means a function's scope is determined by WHERE it's defined in the code, not where it's called.
Every function in JavaScript forms a closure. The inner function has access to:
- Its own variables
- Outer function's variables
- Global variables
Closures are used for: data privacy, factory functions, partial application, memoization, and module patterns.
🏠 Real-world analogy: A closure is like a backpack. When you leave home (outer function), you carry a backpack with items from home (outer variables). Even though you're far from home, you still have access to those items.
💻 Code Example
1// Basic closure2function outer() {3 const message = "Hello from outer!";4 function inner() {5 console.log(message); // Can access outer's variable6 }7 return inner;8}9const fn = outer(); // outer() finishes executing10fn(); // "Hello from outer!" — closure remembers 'message'!1112// Practical: Counter with private state13function createCounter(initialValue = 0) {14 let count = initialValue; // Private — can't be accessed directly!15 return {16 increment: () => ++count,17 decrement: () => --count,18 getCount: () => count,19 reset: () => (count = initialValue)20 };21}22const counter = createCounter();23counter.increment();24counter.increment();25console.log(counter.getCount()); // 226// console.log(count); // ❌ ReferenceError — count is private!2728// Factory functions with closures29function createGreeter(greeting) {30 return function(name) {31 return `${greeting}, ${name}!`;32 };33}34const hello = createGreeter("Hello");35const hola = createGreeter("Hola");36console.log(hello("Alice")); // "Hello, Alice!"37console.log(hola("Bob")); // "Hola, Bob!"3839// The classic loop problem40for (var i = 0; i < 3; i++) {41 setTimeout(() => console.log(i), 100); // 3, 3, 3 (var is shared)42}43for (let j = 0; j < 3; j++) {44 setTimeout(() => console.log(j), 100); // 0, 1, 2 (let creates new binding)45}4647// Memoization using closures48function memoize(fn) {49 const cache = {};50 return function(...args) {51 const key = JSON.stringify(args);52 if (key in cache) return cache[key];53 const result = fn(...args);54 cache[key] = result;55 return result;56 };57}58const factorial = memoize(function f(n) {59 return n <= 1 ? 1 : n * f(n - 1);60});
🏋️ Practice Exercise
Mini Exercise:
- Create a
bankAccountclosure with private balance, deposit, withdraw, and getBalance - Write a
oncefunction that ensures a callback runs only once using a closure - Implement a rate limiter using closures
- Create a function that generates unique IDs using a closure
⚠️ Common Mistakes
Not understanding that closures capture REFERENCES, not values — the value can change after capture
Memory leaks — closures keep references to outer scope alive, preventing garbage collection
The classic
varin loop problem — useletor IIFE to create a new closure per iterationThinking closures are a special syntax — they're just a side effect of lexical scoping and functions
Over-using closures when a class or simple object would be clearer
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Closures & Lexical Scope. Login to unlock this feature.