🎒

Closures

intermediate

A closure is a function that remembers the variables from its outer scope even after the outer function has returned. Think of it as a function carrying a "backpack" of variables wherever it goes.

🎮Interactive Visualization

CodeCreation Phase
1function outer() {
2 let x = 10;
3 function inner() {
4 return x;
5 }
6 return inner;
7}
8
9const fn = outer();
10fn(); // 10
📚Call Stack
Global EC
Variables:
fnundefined
🗄️Heap Memory
(empty)
📤Output
Step 1/6Global EC created - outer function is defined
Key Insight: A closure is formed when an inner function is returned from an outer function, maintaining access to the outer function's variables even after the outer function has completed.

Key Points

  • A closure is created every time a function is created
  • Inner functions have access to outer function variables
  • The outer variables are captured by reference, not by value
  • Closures enable data privacy and stateful functions

💻Code Examples

Basic Closure

function outer() {
  let count = 0;

  return function inner() {
    count++;
    return count;
  };
}

const counter = outer();
counter();  // 1
counter();  // 2
counter();  // 3

Inner function remembers outer variables

Counter Example

function createCounter(start) {
  let count = start;

  return {
    increment: () => ++count,
    decrement: () => --count,
    getValue: () => count
  };
}

const counter = createCounter(10);
counter.increment();  // 11
counter.increment();  // 12
counter.getValue();   // 12

Each call uses the same closed-over variable

Private Variables

function createAccount(initial) {
  let balance = initial;  // private!

  return {
    deposit(amount) {
      balance += amount;
      return balance;
    },
    withdraw(amount) {
      if (amount > balance) {
        return "Insufficient funds";
      }
      balance -= amount;
      return balance;
    },
    getBalance() {
      return balance;
    }
  };
}

const account = createAccount(100);
account.deposit(50);   // 150
account.balance;       // undefined (private!)

Closures enable data privacy

Multiple Closures

function createCounter(start) {
  let count = start;
  return () => count++;
}

const counterA = createCounter(0);
const counterB = createCounter(100);

counterA();  // 0
counterA();  // 1
counterB();  // 100
counterB();  // 101

// Each closure has independent state

Each closure has its own independent state

Loop Closure Bug

// THE BUG: All callbacks share same i
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 100);
}
// Output: 3, 3, 3

// Why? var is function-scoped
// All 3 closures reference the
// SAME i, which is 3 after loop

Classic var loop closure problem

Loop Fix with let

// THE FIX: let creates new binding
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 100);
}
// Output: 0, 1, 2

// Why? let is block-scoped
// Each iteration gets its own i
// Each closure captures different i

let creates new binding per iteration

Common Mistakes

  • Loop variable capture - all callbacks share the same variable
  • Memory leaks from closures holding large objects
  • Forgetting that closures capture by reference, not value

Interview Tips

  • Use the "backpack" metaphor to explain closures
  • Show practical uses: data privacy, function factories, memoization
  • Be ready to solve the classic loop closure problem

🔗Related Concepts