DEV Community

Cover image for Do you know how it works? - JS Closures
Matheus Julidori
Matheus Julidori

Posted on

Do you know how it works? - JS Closures

TL;DR

  • A closure is when a function “remembers” variables from its outer scope.
  • This works even after the outer function has returned.
  • Closures are everywhere: in factory functions, private variables, event handlers, and more.
  • If you understand how scopes work, you already understand 90% of closures.

Introduction

"Closures are scary."

If you’ve been in the JavaScript world long enough, you’ve probably heard this sentence (or thought it).

There’s a certain mystique around closures — like they’re black magic or some abstract wizardry.

But here’s the truth:

If you understand scopes, closures are a breeze.


What Is a Closure?

According to MDN:

“A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function.”

See, that was easy, see y'all next week...
Just kiding. Let's dive deeper on what that sentence means


The apartment

Imagine a function is an apartment.

Inside this apartment, you’ve got furniture (let, const, and var), and maybe a roommate (another function declared inside it).

Now imagine that, after many happy years living together, sharing everything, one day the main tenant moves out (the outer function finishes executing)... but the roommate stays... and somehow still has access to all the furniture, even though the main tenant, the one that has all the furniture's, is long gone.

That roommate is your closure.

The furniture? That’s the variables from the outer scope.

Magic? No. It’s just JavaScript doing its thing.


Example: Accessing Outer Variables

function outerFunction() {
    const message = "I'm from the outer scope!";

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

    return innerFunction;
}

const closureExample = outerFunction();
closureExample(); // "I'm from the outer scope!"
Enter fullscreen mode Exit fullscreen mode

Even though outerFunction() is done executing, innerFunction() still has access to message.
That’s a closure.


Function Factory

Remember HOFs? Well, one of the examples we used there actually makes a good use of closures. I'm gonna use a similar example here, but shorter and easier to visualize.

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

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

console.log(double(5)); // 10
console.log(triple(5)); // 15
Enter fullscreen mode Exit fullscreen mode

In this example, I use the function factory makeMultiplier(multiplier) to return a new function that receives a variable number. The catch is that each returned function remembers the multiplier it was created with.
This is a closure in action — capturing external variables into a private, persistent scope.


Use Case: Private Variables

Another use case for closures is to create a private state in a function.

function secretHolder(secret) {
    return {
        getSecret() {
            return secret;
        },
        setSecret(newSecret) {
            secret = newSecret;
        },
    };
}

const mySecret = secretHolder("I love JavaScript");

console.log(mySecret.getSecret()); // I love JavaScript
mySecret.setSecret("I once said Java is the best");
console.log(mySecret.getSecret()); // I once said Java is the best
Enter fullscreen mode Exit fullscreen mode

You can't access the variable secret directly — only through the returned methods. That's a closure keeping your data safe. Yes, it's very similar to private variables, more on that at the end of the article


So, What’s a Closure Really?

A closure happens any time a function uses variables from its outer scope, even after that scope has finished.

It’s not a special kind of function. It’s just a function with memory. And the best is that you may be using closures for a long time without even noticing, because JS does it on its own.


Why Use Closures?

  • Encapsulate and protect internal state
  • Create configurable functions (factories, currying)
  • Simulate private variables and methods
  • Prevent global pollution
  • Maintain context across function calls

Classes private variables vs Closures private state

Well, there is not much to explain here. They both work similarly, but the historical context may be of more use here.

For years, JavaScript had no true private fields. If you wanted to hide internal state, you’d use closures. Closures are here since the beginning (ES3), and they use logic (function scopes and returned methods) to make something private. The problem is that, with some tricks (reflection) and abuses, you could access those variables.

As of ES2022, JavaScript introduced the # prefix inside classes. They are a new way of truly making something private. The protection is language-enforced(syntax-level), so there is no way of accessing it.

But that does not mean using closures to make something private is "old school bad behavior). It is still very much welcome to do it, specially in specific use cases that may work well with OOP-style encapsulation (Factories, modules, encapsulation in functional style, etc)

Just to make it easier to visualize, here is an example of a function-style encapsulation vs an OOP-style encapsulation:

Function-style (closures)

function createCounter() {
    let count = 0;

    return {
        increment() {
            count++;
        },
        getValue() {
            return count;
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

OOP-style (class with #prefix):

class Counter {
    #count = 0;

    increment() {
        this.#count++;
    }

    getValue() {
        return this.#count;
    }
}

Enter fullscreen mode Exit fullscreen mode

Wrapping Up

Closures might sound complex at first — but they’re just a natural result of how scope and functions interact in JavaScript.

If you can understand who sees what, and when, you can understand closures.


Absolutely! Here’s a concise and friendly message you can include near the end of your post (or as a standalone update) to explain the shift in pace and your exciting plans:


⚡ Quick Message!

It’s been about 10 weeks since I started this series — and honestly, it’s been a blast.
Sharing what I know (and learning in the process) has been super rewarding.

That said… writing weekly is no joke. It takes a lot of time and energy to deliver something valuable and clear every single week.

So here’s the plan:
👉 I’m still going to write — but maybe not every week. I want to focus on delivering content that’s thoughtful and well-planned.

I’m also working on turning this content into a full YouTube series — a complete guide for new devs, from scratch:

  • Programming logic and fundamentals (probably using C++ or some other low-level programming language)
  • Then JavaScript, OOP, and web development
  • Build up to advanced topics, and fullstack development

Think of it as a complete guide — from “What’s a variable?” to “Here’s how to build real-world applications without using AI and Vibe Coding that will break your app due to security and bad code.”

And I'd love your support on this project. It may take a while for it to really start and get a consistency on posts, but if you want to support me, start by subscribing to my channel.

Thanks for following along so far. I appreciate all the support, messages, and comments. You are the reason I keep doing this, and I hope you stick with me on this next chapter too! 😊🙌

Let’s keep learning — together. 💻📚🎥


💬 Got a clever use case for closures? Or a bug caused by one?
Drop it in the comments — I’d love to hear about it.

📬 If you're enjoying this series, save it or share it with a fellow JS dev. Let’s keep learning together!

👉 Follow me @matheusjulidori for more content on JavaScript and web development!

🎥 And subscribe to my YouTube channel to catch the next chapter — from the very beginning.

Top comments (0)