In this article, we’re going to learn about a concept that’s fundamental in JavaScript development today: Closures. We’re going to understand how Closures work under the hood, why this concept is so popular among developers, and how we can benefit from using it in our code.
If you’ve been using JavaScript in your projects or in your company for a while, you might have heard about Closures. A lot of developers like this concept, but it seems like Closures are still a mystery. Sometimes developers might even be using it daily in their code and don’t know it yet! The problem with Closures is they seem like a mystery between developers — sometimes a developer might even know more or less how a Closure works but not know exactly how to explain it.
The best way I’ve found to learn or study something is to read everything related to a topic and practice implementing it, and then try to explain it to another developer. I have found that often it seems simple to explain a concept to another developer because I know how it works myself, but finding the right words to explain it to another person is far more complicated.
That’s why we’re going to dive deep in this article and learn everything we can about Closures, and then write some cases where we can see the benefits. After that, you’ll be able to explain in a simple sentence what Closures in JavaScript are and how they work. So, let’s get started!
In JavaScript, to understand about Closures, we first need to understand a little bit about Scope and how it works.
Every time you declare a new function in JavaScript, that means you’re creating a new function scope. For example, let’s imagine that we’re going to create the following function:
function sayMyAge(yearOfBirth: number) {
const year = 2019;
const myAge = year - yearOfBirth;
return `Your age is: ${myAge}`;
};
As you can see, the sayMyAge
function is simply making a calculation and returning the age of a specific person. Inside that function, we have a variable called year
which is the current year, and the myAge
variable which is our age calculated.
Since this function has its own scope, and as we know, in JavaScript all the variables and functions that we have inside a function will belong to that function, if we try to call it outside of our function, we’re going to receive an error.
console.log(year); // ReferenceError
console.log(myAge); // ReferenceError
What exactly does this error tell us? Well, it tells us that these specific variables are not available to the global scope, and that’s why we’re receiving this error. We’re trying to return something that’s not available on the global scope, only in a function scope, in our case, the sayMyAge
function scope.
One of the benefits of using a function scope to hide variables and functions is that you can avoid collision between variable names and also functions. If you have a collision between values in your code, that means you can have unexpected overwriting of values.
Now that we know a little bit about Scope in JavaScript, let’s learn more about Closures and how they work.
To understand exactly how a Closure works, we’re going to use our sayMyAge
function and make some changes. First, we’re going to create a new function inside our sayMyAge
function, and this function is going to be called logAge
.
We’re going to move a part of our function inside that new logAge
function, and the year
variable that we had before, we’re going to maintain inside the inner scope of sayMyAge
. So, our sayMyAge
function is going to look like this:
function sayMyAge(yearOfBirth: number) {
const year = 2019;
function logAge(yearOfBirth) {
const myAge = year - yearOfBirth;
return `Your age is: ${myAge}`;
}
return logAge;
};
As you can see, we moved our year variable to the inner scope of the sayMyAge
function. Then, we created a new function called logAge
, which is going to return our age, based on a calculation made using the year
variable, and the yearOfBirth
which is our function parameter.
Now, on our global scope, we’re going to create a new variable called age, and assign to this variable our sayMyAge
function, like this:
const age = sayMyAge;
If we try to console this age variable, we’re going to receive our sayMyAge
function value, since we’re not invoking it.
const age = sayMyAge;
console.log(age);
// Result:
//
So now, let’s make a quick change and see what happens. First, let’s redeclare the age
variable; instead, this time, we’re going to invoke the sayMyAge
function. Also, when we console the age
variable, we’re going to invoke it this time and pass a year to it. Let’s see what happens.
const age = sayMyAge();
console.log(age(1997));
// Result:
// Your age is: 22
We just created a Closure! You might not know exactly what happened here and how we ended up creating a Closure, so let’s go to it and understand what happened step by step:
sayMyAge
function, with a year
variable inside of its inner scope. Also, inside of its inner scope, we created a function called logAge
, which uses the year
variable to calculate our age.logAge
function uses its parent scope, which is the sayMyAge
scope, and gets the year variable from there. Inside our sayMyAge
function, we’re returning just the logAge
function.sayMyAge
function to the age
variable, we have lost the inner scope of sayMyAge
. That means that, in theory, we shouldn’t have access to the year
variable. But in JavaScript, our inner functions still reference values created inside the scope, in our case the sayMyAge
scope.age
and pass an argument to it, it executes the logAge
function, referencing and using the year
variable that once was declared inside the sayMyAge
scope.Basically, a Closure is a function inside another function, that has access to its parent scope, and is exposed to the global scope. What does this mean? It means that this function will be used in another part of the code, making the variables inside of it private.
Pretty neat, a not-so-simple concept to catch at first, but when you do realize what’s going on under the hood, you start to admire the beauty of JavaScript!
Let’s create another example to understand Closures even better. We’re going to create a function called favoriteMovie
, which is going to return the favorite movie of an individual.
Our favoriteMovie
function is going to be similar to the last one that we created, so this time, if you didn’t yet catch on to the magic about Closures, hopefully you can have your a-ha! moment.
Our favoriteMovie
function is going to look like this:
function favoriteMovie(name: string) {
function myFavoriteMovie(movie: string) {
return `Hi ${name}, your favorite movie is ${movie}!`;
}
return myFavoriteMovie;
}
As you can see, our favoriteMovie
function receives a name
as an argument, and returns a function called myFavoriteMovie
. This function is receiving a movie
as an argument, and returning a string with the name
and movie
passed. In the other function that we created, we had a variable declared inside the parent scope — in that case, the sayMyAge
function. In this case now, we’re not going to have any variable declared.
Now, let’s create a variable called user
and pass to it the favoriteMovie
, passing a name
to it.
const user = favoriteMovie("Leonardo");
Now that we passed a name to our function, we’re going to invoke the user function and pass our movie, like this:
console.log(user("Pulp Fiction"));
// Result:
// Hi Leonardo, your favorite movie is Pulp Fiction!
The only way that you can master Closures and understand it deeply is by practicing! So, start to create a lot of functions, and try to build something complex and apply this concept in code. See how it’ll lead you to write code that’s more concise, clearer and simpler.
Closures in JavaScript are pretty neat. They’re not so simple a concept at first glance, but as you learn what’s going on behind the scenes, the elegance of JavaScript really shines!
In this article, we learned about scope in JavaScript at first, and then we learned about Closures in JavaScript — how they work, and how we’ve been using them all over our code for a long time and didn’t know it. Closures are also an important concept to master for the interview process, so if you’re hoping to get a new job offer soon, it’s a very good concept to master and understand. Plus, you’re going to use them all over your code, and after you master them, it’ll get easier to code and create awesome things.
Looking for more insights on JavaScript fundamentals? Check out JavaScript Fundamentals: Prototype Chains next.
Leonardo is a full-stack developer, working with everything React-related, and loves to write about React and GraphQL to help developers. He also created the 33 JavaScript Concepts.