Closures in JavaScript

Alexandru Știube
Yonder TechBlog
Published in
4 min readMay 9, 2023

--

In JavaScript, we don’t have public or private keywords for methods. However… We have public and private things.

Photo by Mohammad Rahmani on Unsplash

Let’s imagine the next code, I’ll explain it later, first let’s just look at it:

function createCounter() {
let count = 0;

function increment() {
count++;
}j

function decrement() {
count--;
}

function getCount() {
return count;
}

return {
increment,
decrement,
getCount,
};
}

We have the function createCounter and inside of it we have a local variable and three methods. Also, the function returns an object that contains all of the methods. Pay attention to the fact that we are not calling the functions in the return statement, we are just writing there the name of the function.

Now, let’s use our code. First, we need to create our counter and then use it. It will look something like this:

const counter = createCounter();

console.log(counter.getCount()); // Output: 0
counter.increment();
counter.increment();
console.log(counter.getCount()); // Output: 2

In a variable we save what createCounter returns, exactly the three functions from the return object. Then we can use the inner functions as in the example. If we would have other method in createCounter that is not returned, that method would remain private and we can’t use it outside of the initial scope and, all the methods that are returned are public.

The concept of closure is a combination of functions that are enclosed in a scope. Our methods and the variable are enclosed in the local scope of createCounter. You can imagine this as the “close one” inside the scope. Also a closure is created at function creation time.

Closures work hand in hand with the lexical scope, this is based on the structure of the code, which defines the nested blocks of code, such as functions or statements within other functions or blocks. When a variable or function is defined within a particular block of code, it is said to be in the scope of that block.

// Global scope
let a = 1;

function outer() {
// Outer function scope
let b = 2;

function inner() {
// Inner function scope
let c = 3;

console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
}

inner();
console.log(a); // 1
console.log(b); // 2
console.log(c); // ReferenceError: c is not defined
}

outer();
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
console.log(c); // ReferenceError: c is not defined

We have the global scope, which includes basically everything, and inside it, we have smaller local scopes, or function scopes or whatever you like to name them. The idea is that if you have something between {} it has a scope. In the previous example we can easily see how this works when writing code.

If we try to use something from inside a scope outside of that scope, we will get an error. If we want to use something on the outer scope, we have to return it, or we can use var, but we don’t use var exactly because we don’t want to have a variable everywhere. Keep in mind that there are other reasons why we don’t use var but let’s stick to only this for the current article. Just avoid using var.

And now let’s talk about a function that directly returns another function, sounds fun right?

const factory = () => {
const text = "I am just a string";
const betterText = "I am still just a string";

console.log(text);

return () => {
console.log(betterText);
};
};

const thing = factory();
thing();

Here we have something interesting. Let’s start with how we declare the function. We basically save the function declaration inside the factory variable. In JavaScript functions are just like any other object, so, a function can be passed as arguments to other functions, returned as values from functions, and stored in variables or data structures.

Inside factory we declare two constants and we log to the console the first variable. Then, in return things gets interesting again, here we return an anonymous function, its anonymous because it doesn’t have a name, and it doesn’t even need one, because we can use name for it later.

Now that we have understand what’s inside factory, we can focus on how we can use it. First, in a variable we save what factory returns, but also at this step the function is executed, and it will log “I am just a string” to the console. And on the last line, we call our previously declared variable.

Because thing (I am bad at naming) has what factory returned we can call it essentially because factory returned a function. At this point the other string, “I am still just a string” is logged to the console.

This approach of using functions inside another functions is often used in libraries or modules from npm. If you ever opened node_modules there is a good chance that you have already see a function that returns another function, besides other weird stuff maybe.

Also, we can use this if we want (or need) to have public or private methods around our apps. The concep is pretty useful and there are endless possibilities of how can be used. And keep in mind that there is a good change to get a question about this at interviews if you applied for a possition based on JavaScript.

JavaScript can be weird sometimes and that’s why we may need to look two times how a function works. If you would like to go deeper with this subject, here is the Mozilla Web Docs MDN page about closures and this page from GeeksForGeeks.

--

--

Alexandru Știube
Yonder TechBlog

A software engineer that likes to learn new things and sometimes teach others