From the words of Kyle Simpson,
“Closure is all around you in JavaScript, you just have to recognize and embrace it.”
When you write in JavaScript, closures just happen… wether you are aware of it or not. Understanding closures to intentionally create them and leverage their power is a challenge that every JavaScript developer has to tackle; let’s give it a try with this article.
Here’s a somewhat academic definition that will help you understand and spot a closure when you see one : closure is when a function is able to remember and access the variables of the outer (enclosing) function even when that function is executing outside its scope.
Closure is when a function is able to remember and access the variables of the outer (enclosing) function even when that function is executing outside its scope.
We will go back to the scope concept a little later. Just know for the moment that, depending on the position of a variable in the code, some functions will have access to it and some will not.
Now about the variables; as you already may know, a variable has two components : a name and a value. The name “variable” makes it very clear : its value can vary from one moment to the next. But sometimes, we need to keep the value as it was at a certain point in time to use it later.
In JavaScript, this situation is frequently encountered when working with functions like setTimeout()
and setInterval()
for example.
for
loop exampleLet’s take the example of a for
loop used to display a counter, and a setTimout()
function used to delay the execution of a function.
Looking at this code snippet, you would normally assume that it would print the numbers 0 to 9, one per second. In fact if you run this code, you get 10 printed out ten times, at a one-second interval :
Why is that happening ?
When the for loop runs and setTimeout()
gets called sometime in the future (in that case, one second later in the future), the value of the variable i
has already been incremented to the end of its range by the for loop.
This is a case where we need to store and access the value of a variable at a given time, before it is modified again. At each iteration loop, we need to “capture” the corresponding copy of i
and store it for later use. The good news is that closures are going to help us do just this : create a new closured scope at each iteration of the loop.
We will get back to this example and see how to add a closure to our loop. But before diving deeper inside the workings of closures, let’s take a brief detour and review some important concepts we need to understand well to get a full grasp of closures.
A function is a special variable : you can reassign a value to it and pass it to another function as parameters (this is useful in writing asynchronous functions which are passed as callbacks arguments).
Variables that are defined outside of a function can be accessed by that function. They can be modified outside of that function as well as by the function itself.
Variables that are defined inside of a function, as well as the arguments passed to a function, are only accessible inside the function.
(To learn more about the scope, please refer to this article)
When a variable is passed as an argument to a function, the value of the variable is copied inside the argument.
Now that we finished our detour and got back, we are ready to tackle closures.
Let’s consider the following code :
Let’s dive a little deeper into this function : what’s going on here ?
We define an argument number1
that will take the value of the variable passed to the function.
We define a function inside the function myFunction
. In this nested function, the variable number1
is accessible because it has been defined outside of the add
function, in its parent function myFunction
.
We return the add
function, but we are not calling it. What does this mean ?
The myFunction
function returns a function : the add
function. When I print myFunction
to the console, it will display the return result of the function, which is another function (the add
function) :
Why is this important ? Because it means that I can assign a value to number1
, pass it to myFunction
, and this new value of number1
will be stored for later use, when I will be ready to finish the job and call the add function.
And here is the awesome thing about closures : you can write functions with an intermediate state that can capture data from your application, at a very specific moment in time, and you can then use this data at another, later moment in time. Using closures is like adding a “pause” button inside your function. You can go back to it later, when there has been a change in your app (a click event from the user for example), and still be able to retrieve the value of your data before this change in the application happened.
Without closures, JavaScript would simply run all the code and return the last known value of your data, with no way to go back to the moment when the data had the value you now want to use.
That’s what happened in our first for
loop example : the setTimeout
function kind of arrived late for the war. The battles of the for
loop had already been fought and when the setTimeout
cavalry arrived, the i
variable had long been assigned 10, the latest value in the loop, and stayed that way until setTimeout
arrived.
Still not convinced or not sure when to use a closure ? Let’s take an example and explain how closures work by baking a cake.
We are going to use this code that uses a function to bake cakes that have different ingredients and cooking temperatures :
The ovenTemperature()
function is a closure, a function inside a function, that can be called at any point in time after the bakeCake()
function has been called.
Notice how we have to take two steps to get the whole log in the console ? If your run this code and never call chocolateCake()
or carrotCake()
, the console will only print :
"chocolate cake : add chocolate to the batter""carrot cake : add carrot to the batter"
You wouldn’t get any error, but the function inside the function, the closure, would not run and the completion of baking the cake would not happen.
Notice also that I can use my bakeCake()
function to bake two very different cakes, each one being a separate instance of bakeCake()
that will remember its own ingredient
argument for later use.
Like in a real recipe, it is not enough to just add the ingredient
to the batter, you also have to set the right temperature
and baking time
to have a perfect cake. And for that, you have to call another function inside the function. As we saw in the earlier example with the add()
function, if this inner function isn’t called, the return of the outer function is simply another function, waiting for its time to be called, not a result value (yet).
Here, the bakeCake()
function will not return the "ready to bake" line until you also call the ovenTemperature()
function with the two arguments : it is on hold until called properly.
You can put the chocolate in the batter, let the batter rest, and take all the time you need to check your recipe book for the right temperature and baking time. It can be an hour later, you may have to call your mother to get advice on it, and the batter with chocolate will still be here, waiting for you to give the final instructions. In other words, anytime you will call chocolateCake()
, the ingredient
argument, chocolate, will still be incorporated.
So how do I call this inner function when I am ready ? I’ve called my mother, checked every recipe on the internet, now let’s finish off these cakes, once and for all.
I have created two instances of the bakeCake()
function and assigned them to two different variables : chocolateCake
and carrotCake
.
Let’s concentrate on the chocolate cake. chocolateCake
is a function, and an instance of bakeCake
, with a chocolate
argument. For chocolateCake
to return the "ready to bake" sentence, I just have to call it and pass the arguments needed by the ovenTemperature()
function.
This means that chocolateCake
will not be fulfilled until we pass a second set of arguments for the closure to be triggered.
Here for simplicity of comprehension we first assigned bakeCake("chocolate")
to a variable (chocolateCake
), and then passed the second arguments to this variable, which is also a function. But if we already knew all the requirements of the recipe, we could have gone directly :
bakeCake("chocolate")(250, 60);
The ultimate take on this experiment, in the words of Douglas Crockford, is that:
“An inner function always has access to the vars and parameters of its outer function, even after the outer function has returned.”
setTimeout()
inside a for
loop exampleNow with all we know from the bakeCake()
example, how can we modify the code from our first example to add a closure and make the function print the numbers from 0 to 9 instead of printing the number 10 ten times? ( remember 10 is the last known value of i
: on the last iteration of the loop, the function logged 9 in the console, then incremented this number by 1.
Let’s have a look at this function in real time :
Here is the code of our function :
For comparison purposes, we added a simple console.log()
for the i
counter at each iteration of the loop, and a setTimeout()
function that is supposed to do the same, this time at 1 second intervals.
From the image above we can easily see that that the setTimeout()
function does indeed print a result at one second intervals, but the increment of i
has already taken place and when setTimeout()
begins to run, i
has an unchanged value of 10 at each delayed iteration of the loop.
How are we getting out of this ?
Let’s apply what we have learned from using closures so far, and write a timer()
function that will act as the outer function for setTimeout()
, from which setTimeout()
will be able to access the value of i
:
And… it works !
Here we declared a timer()
function and wrapped the setTimeout()
function inside of it. We then pass it the exact value of the counter at each iteration of the loop, hereby "closing" the value of **i**
inside the function at each iteration.
We could also have written :
where we declare an anonymous self-invoking function and pass it the argument i
at each iteration of the loop.
This will print the number 0 to 9 at one second intervals, thanks to the closure we created : we captured the value of **i**
at a given iteration of the loop, we closed it inside a function, passed the value to **setTimeout()**
and then called the function.
Closures on the front-end can help us achieve a lot of things, like passing parameters on a click event from the user or overcoming the google maps API results limit.
On your journey to becoming an intermediate or advanced JavaScript developer, you will come across closures and now be able to spot them, hopefully use them, and understand some bugs that wouldn’t make sense otherwise.
Want to learn more ? Check out my other articles on the basics of JavaScript:
I hope you enjoyed this introduction to JavaScript closures.
Feel free to comment and like this article so that others can find it easily on Medium !