JavaScript Closures

Learn how inner functions remember variables from their parent scope forever, keep private data safe, and avoid common loop timer traps.

1. What is a Closure?

A closure happens when an inner function remembers and has access to variables from its parent function, even after the parent function has finished running and closed.

💡 Think of a Function Carrying a Travel Backpack

When an inner function is born inside a parent function, it packs all the parent's variables into its magical backpack. Wherever that inner function travels later in your code, it still carries and can use everything inside that backpack!

function createGreeting(greetingWord) {
    // greetingWord is in the parent scope
    return function(personName) {
        // The inner function remembers 'greetingWord' forever!
        return `${greetingWord}, ${personName}!`;
    };
}

const sayHello = createGreeting("Hello");
const sayNamaste = createGreeting("Namaste");

// createGreeting() is already finished, but sayHello still remembers "Hello":
console.log(sayHello("Alex"));    // "Hello, Alex!"
console.log(sayNamaste("Priya")); // "Namaste, Priya!"
Output
Hello, Alex!
Namaste, Priya!

2. Keeping Data Private (Encapsulation)

In JavaScript, variables inside a function are protected. By returning methods that interact with them, you can create private counters and secure state:

function createCounter() {
    let count = 0; // 🔒 Private variable trapped inside the closure!

    return {
        increment() {
            count++;
            return count;
        },
        decrement() {
            count--;
            return count;
        },
        getCount() {
            return count;
        }
    };
}

const counterA = createCounter();
const counterB = createCounter(); // Has its own separate private count!

console.log(counterA.increment()); // 1
console.log(counterA.increment()); // 2
console.log(counterB.increment()); // 1 (Isolated from counterA!)

// count cannot be modified directly from outside:
// console.log(counterA.count); // undefined (Safe!)
Output
1
2
1

3. The Classic Loop + Timer Closure Trap

This is one of the most famous JavaScript interview questions in tech:

The `var` Shared Scope Trap ❌

var shares a single variable across all loops. By the time the timers run, i is already 3.

for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 50);
}
// Outputs: 3, 3, 3

The Modern `let` Solution ✅

let creates a fresh new block scope on every single iteration, saving the exact current value into its closure.

for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 50);
}
// Outputs: 0, 1, 2

4. Real-World Use Case: Function Caching (Memoization)

Closures let you remember previous results so you never have to re-run expensive calculations twice:

function createMultiplier(multiplier) {
    // Remembers 'multiplier' in its closure:
    return function(number) {
        return number * multiplier;
    };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(10)); // 20
console.log(triple(10)); // 30
Output
20
30

5. Quick Closure Summary

Concept How it Works Main Benefit
Lexical Memory Inner functions preserve outer variables Data persists without global variables
Data Privacy Variables stay locked inside the outer function Prevents accidental external modification
Function Factories Functions that generate custom functions Reusable, configurable logic (e.g. double, triple)

6. Closures Interview Deck

Test your knowledge on closure persistence, multiple instances, loop bindings, function factories, and variable isolation:

🎯 Closures Interview Deck
Question 1 of 5
⭐ Easy • Independent Closure Scopes Closures & Memory
function outer() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}

const fn1 = outer();
const fn2 = outer();

fn1();
console.log(fn1(), fn2());
What will be logged to the console?
A) 2 1
B) 2 2
C) 3 1
D) 1 1
⚡ Medium • var + setTimeout Loop Trap Event Loop & var
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 0);
}
What will be printed when the timers execute?
A) 0, 1, 2
B) 3, 3, 3
C) undefined, undefined, undefined
D) 0, 0, 0
⭐ Easy • Shared Closure Reference Encapsulation
function createStore() {
    let value = 100;
    return {
        add(n) { value += n; },
        get() { return value; }
    };
}

const store = createStore();
store.add(50);
console.log(store.get());
What is the output of store.get()?
A) 150
B) 100
C) 50
D) undefined
⚡ Medium • Currying & Function Factory Currying
function multiply(x) {
    return function(y) {
        return x * y;
    };
}

const double = multiply(2);
console.log(double(5), multiply(3)(4));
What will be printed to the console?
A) 10 12
B) 10 7
C) 7 12
D) undefined 12
⭐ Easy • Lexical Isolation Data Protection
function makeSecret() {
    let code = "1234";
    return () => code;
}

const getSecret = makeSecret();
let code = "9999";

console.log(getSecret());
What will be logged to the console?
A) "1234"
B) "9999"
C) undefined
D) ReferenceError

7. Practice Challenge

🎯 Coding Challenge: Secure Bank Account Factory

Build a createBankAccount(initialBalance) function factory that encapsulates a private balance variable. Provide deposit(amount), withdraw(amount), and getBalance() methods that manipulate the balance securely.

Your output will appear here.