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:

  1. Its own variables
  2. Outer function's variables
  3. 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

codeTap to expand ⛶
1// Basic closure
2function outer() {
3 const message = "Hello from outer!";
4 function inner() {
5 console.log(message); // Can access outer's variable
6 }
7 return inner;
8}
9const fn = outer(); // outer() finishes executing
10fn(); // "Hello from outer!" — closure remembers 'message'!
11
12// Practical: Counter with private state
13function 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()); // 2
26// console.log(count); // ❌ ReferenceError — count is private!
27
28// Factory functions with closures
29function 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!"
38
39// The classic loop problem
40for (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}
46
47// Memoization using closures
48function 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:

  1. Create a bankAccount closure with private balance, deposit, withdraw, and getBalance
  2. Write a once function that ensures a callback runs only once using a closure
  3. Implement a rate limiter using closures
  4. 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 var in loop problem — use let or IIFE to create a new closure per iteration

  • Thinking 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.