DEV Community

Cathy Lai
Cathy Lai

Posted on

Closures

What is a closure?

A closure happens when a function “remembers” the variables from the scope where it was created, even after that scope has finished running.

In other words:

👉 A function carries around a backpack of variables from the place it was born.


1. Simple example

function outer() {
  const message = "Hello from outer!";

  function inner() {
    console.log(message); // uses variable from outer scope
  }

  return inner;
}

const fn = outer();
fn(); // "Hello from outer!"
Enter fullscreen mode Exit fullscreen mode
  • inner is returned out of outer.
  • Even though outer has finished, inner still has access to message.
  • That’s the closureinner “closes over” the message variable.

2. Closure with parameters

function makeMultiplier(multiplier) {
  return function (x) {
    return x * multiplier;
  };
}

const double = makeMultiplier(2);
console.log(double(5)); // 10

const triple = makeMultiplier(3);
console.log(triple(5)); // 15
Enter fullscreen mode Exit fullscreen mode
  • double remembers multiplier = 2.
  • triple remembers multiplier = 3.
  • Each returned function has its own closure environment.

3. In React hooks

Closures explain why we can do this:

const greeting = useMemo(() => {
  return `Hello, ${name}`;
}, [name]);
Enter fullscreen mode Exit fullscreen mode
  • The () => { ... } factory closes over the current value of name.
  • React doesn’t pass name into the function — the closure makes it available.
  • That’s why we must put name in the dependency array, so React knows when the closed-over value might change.

4. Analogy 🎒

Imagine a function is a traveler.

  • When it’s created, it packs a backpack with the variables in scope.
  • Later, no matter where it goes, it can unzip the backpack and use those variables.

Java vs JavaScript analogy

Java local variables

In Java:

void outer() {
    String message = "Hello";

    class Inner {
        void print() {
            System.out.println(message);
        }
    }

    Inner i = new Inner();
    i.print(); // prints "Hello"
}
Enter fullscreen mode Exit fullscreen mode
  • message is a local variable of outer().
  • Inner classes in Java can “capture” these variables, but Java enforces them to be effectively final (not reassigned after creation). So inner classes in Java have a limited version of closure.

JavaScript closures

In JS:

function outer() {
  let message = "Hello";

  function inner() {
    console.log(message);
  }

  return inner;
}

const fn = outer();
fn(); // "Hello"
Enter fullscreen mode Exit fullscreen mode
  • message is a local variable of outer().
  • inner keeps a live reference to message even after outer has finished.
  • Unlike Java, JavaScript allows closures over variables that can change:
function outer() {
  let count = 0;

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

const inc = outer();
console.log(inc()); // 1
console.log(inc()); // 2
console.log(inc()); // 3
Enter fullscreen mode Exit fullscreen mode

Here count behaves like private state — accessible only through the closure.


Key difference

  • Java local variables (in lambdas/inner classes) → captured by value, must be effectively final.
  • JavaScript closures → captured by reference, can update over time, and the function “remembers” the variable for as long as it’s alive.

Analogy

  • In Java: closure = you take a snapshot of a local variable at the time the inner class/lambda was created.
  • In JS: closure = you keep a live wire to the variable itself — if it changes later, your function sees the updated value.

✅ So closure variables look like local variables in Java, but in JavaScript they are kept alive and mutable by the function that closes over them.

Top comments (0)