JavaScript Closures: The Quiet Mechanism Running Your Code

Published: (December 28, 2025 at 12:26 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

I avoided closures for longer than I care to admit.

Not deliberately. I just kept telling myself I understood them because I could repeat the usual line: “a closure is a function that remembers its lexical scope.” That sentence sounds convincing. It is also not enough.

Closures only really make sense once you stop treating them as a concept to memorise and start seeing them as a behaviour your code exhibits.

This article is not a definition dump. It is an attempt to explain closures the way they actually show up in real JavaScript code and why they matter far more than most beginners realise.

A closure is not created when you write a function

Closures are not created when a function is declared. They emerge when a function is executed and continues to reference variables from an outer scope that would otherwise be gone.

function createCounter() {
  let count = 0;

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

const counter = createCounter();

At first glance, it looks like count should disappear as soon as createCounter finishes running. But it does not. The inner function still has access to it.

Why? Because JavaScript does not copy values into functions. It keeps references to the environment in which the function was created. That environment, the lexical environment, is what we casually call a closure.

Closures are about memory, not syntax

One of the most useful mental shifts is realising that closures are fundamentally a memory model, not a syntax feature. The JavaScript engine keeps variables alive only as long as something can still reach them.

In the example above:

  • count is still reachable
  • therefore it stays in memory
  • therefore its value persists between function calls

That is it. No magic. Once you see closures this way, a lot of confusing behaviour suddenly becomes predictable.

Why closures feel confusing at first

Closures break an intuitive rule many people unconsciously follow:

“When a function finishes, everything inside it disappears.”

That rule is mostly true—until it isn’t. Closures are the exception that proves the rule. They force you to think about who still has a reference to what. This is also why closures are a common source of bugs, especially in loops.

for (var i = 0; i  {
    console.log(i);
  }, 1000);
}

People expect 0, 1, 2. They get 3, 3, 3. Not because JavaScript is broken, but because all three functions close over the same variable, not three different values. Once you understand closures, this behaviour stops being surprising.

Closures are everywhere, even when you do not notice them

If you have ever:

  • used useState in React
  • written a callback
  • used setTimeout or setInterval
  • worked with event listeners
  • implemented currying or partial application

you have already used closures.

Frameworks lean heavily on closures because they allow state to exist without global variables—a huge design advantage. React hooks, for example, rely on closures to “remember” values between renders while still keeping functions pure in appearance.

Closures enable encapsulation without classes

Before ES6 classes became popular, closures were one of the main ways to create private state in JavaScript. Even today, many developers prefer closures over classes for simple encapsulation:

function createBankAccount() {
  let balance = 0;

  return {
    deposit(amount) {
      balance += amount;
    },
    getBalance() {
      return balance;
    }
  };
}

Here, balance is completely inaccessible from the outside. There is no keyword for privacy; the privacy emerges naturally from the closure. This pattern still holds up remarkably well.

The downside: closures can keep things alive too long

Closures are powerful, but they are not free. Because they keep references alive, they can accidentally keep large objects in memory longer than necessary—especially in long‑running applications like browsers or servers. Memory leaks caused by closures are subtle and often misunderstood. The problem is not closures themselves, but forgetting that what you reference stays alive.

Understanding closures helps you reason about memory usage far better than memorising garbage‑collection rules.

A better way to explain closures (in one sentence)

A closure is what happens when a function keeps a live connection to variables that existed when it was created.

That sentence is not poetic, but it is accurate.

Reading further and deepening your understanding

If you want to go deeper, here are some genuinely useful directions to explore:

  • MDN’s explanation of lexical environments and execution contexts
  • Articles that explore JavaScript engine internals (V8 in particular)
  • Functional programming discussions on closures in Scheme and Lisp
  • Academic papers on lexical scoping and environment models (arXiv is useful here)

Closures are not unique to JavaScript. Understanding them well makes learning other languages easier.

Final thoughts

Closures are not something you “learn once”. They are something you gradually notice more clearly the longer you write JavaScript. At some point, they stop being a mysterious interview topic and start becoming a quiet, dependable tool you rely on without thinking. That is usually the sign that you finally understand them.

Back to Blog

Related posts

Read more »

The Secret Life of JavaScript: Memories

The Ghost Room: A Story of Closures and the Variables That Refuse to Die. Timothy stood in the doorway of a small, private study off the main library hall. He h...