DevRoadmap
JavaScript

JavaScript Closures Explained (Finally Making Sense)

Closures come up in every JavaScript interview and appear in every real codebase. This guide explains them in plain English, then shows you the practical patterns where you're already using them.

READ TIME 7 min read
CATEGORY JavaScript
Advertisement

What is a Closure?

A closure is a function that remembers the variables from its outer scope even after that outer function has finished executing. That's it. The "closed over" variables persist as long as the inner function exists.

This sounds abstract until you see it in code. Let's start with the simplest possible example and build from there.

function makeGreeter(name) {
  // 'name' is a local variable in makeGreeter
  
  return function() {
    // This inner function closes over 'name'
    // It can access 'name' even after makeGreeter has returned
    console.log(`Hello, ${name}!`);
  };
}

const greetAli = makeGreeter("Ali");
const greetSara = makeGreeter("Sara");

greetAli();  // "Hello, Ali!"
greetSara(); // "Hello, Sara!"

// makeGreeter has finished running, but 'name' still lives
// inside each returned function — that's a closure.

Understanding Scope First

To understand closures, you need to understand lexical scope. In JavaScript, a function has access to variables defined in:

  1. Its own local scope
  2. The scope of any outer function it's nested inside
  3. The global scope

Closures are the mechanism by which functions carry references to their outer scope — not just at the time they were created, but for as long as they exist.

let globalVar = "I'm global";

function outer() {
  let outerVar = "I'm from outer";
  
  function inner() {
    let innerVar = "I'm from inner";
    
    // inner can access all three:
    console.log(globalVar);  // ✅
    console.log(outerVar);   // ✅ — this is the closure
    console.log(innerVar);   // ✅
  }
  
  inner();
}

// outer cannot access innerVar
// global cannot access outerVar or innerVar

Practical Closure Patterns

Counter Factory

function createCounter(startValue = 0) {
  let count = startValue; // private — can't be accessed from outside
  
  return {
    increment: () => ++count,
    decrement: () => --count,
    reset: () => { count = startValue; },
    getCount: () => count
  };
}

const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.reset();     // back to 10
console.log(counter.count); // undefined — count is private!

Memoization (Caching Expensive Results)

function memoize(fn) {
  const cache = {}; // closed over — persists between calls
  
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key] !== undefined) {
      console.log("Returning cached result");
      return cache[key];
    }
    cache[key] = fn(...args);
    return cache[key];
  };
}

const expensiveCalc = memoize((n) => {
  // Imagine this takes 2 seconds
  return n * n;
});

expensiveCalc(10); // calculates
expensiveCalc(10); // returns cached — instant

The Classic Closure Interview Trap

// ❌ Common bug — all buttons alert the same number
for (var i = 0; i < 3; i++) {
  document.querySelectorAll("button")[i].addEventListener("click", function() {
    alert(i); // always alerts 3 — i is shared!
  });
}

// ✅ Fix 1: Use let (block-scoped, creates new binding per iteration)
for (let i = 0; i < 3; i++) {
  buttons[i].addEventListener("click", () => alert(i)); // alerts 0, 1, 2
}

// ✅ Fix 2: IIFE (Immediately Invoked Function Expression)
for (var i = 0; i < 3; i++) {
  (function(capturedI) {
    buttons[capturedI].addEventListener("click", () => alert(capturedI));
  })(i); // immediately invoked with current i
}
Interview TipWhen asked to explain closures in an interview, lead with the definition: "A closure is a function that has access to its outer function's variables even after the outer function has returned." Then walk through a concrete counter example. Interviewers are testing whether you understand scope and memory, not whether you've memorized a textbook definition.
Advertisement