← all posts

JavaScript closures explained by someone who actually uses them

·2 min read ·JavaScript · Programming · Fundamentals

Closures get introduced in JavaScript courses right before the interviewer asks you to explain them. They feel academic. They aren't—I use closures constantly in production code.

What a Closure Actually Is

A closure is a function that remembers the variables from the scope where it was defined, even after that scope has closed.

function createCounter() {
    let count = 0; // This variable is 'closed over'
    
    return {
        increment: () => ++count,
        decrement: () => --count,
        value: () => count,
    };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
counter.value();     // 1

count is private to the closure. Nobody can access it directly. This is how you get encapsulation in JavaScript without classes.

Memoization

function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) return cache.get(key);
        
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

const expensiveCalc = memoize((n) => {
    // Heavy computation...
    return result;
});

The cache Map is closed over by the returned function. It persists across calls without being a global variable.

The Classic Trap

for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 0);
}
// Prints: 3, 3, 3 (not 0, 1, 2)

All three callbacks close over the same i. By the time they run, the loop has finished and i is 3. Use let instead of var and each iteration gets its own block-scoped i.

Closures are everywhere once you know to look: React hooks, event handlers, module patterns, partial application. They're not a trick—they're a fundamental building block.