DEV Community

loading...
Cover image for What's a closure?

What's a closure?

kjdowns profile image Kevin Downs ・5 min read

Learning through teaching

As I mentioned in my previous post, I am a recent graduate from Flatiron School's Full Stack Software Engineering program. Like a lot of other people starting their job search, I have been spending some time learning more and preparing for what I consider the most intimidating part of the process - the technical interview. As I was doing more practice, between mock interviews and practice problems, I started to notice a trend. I had experience with many of the concepts presented, generally knew how to use them and how they worked, but lacked the industry jargon to recognize them by their name when asked to explain specific concepts. I decided it would be beneficial for me to dive into these terms in order to be better prepared in the future.

I was always told that the best way to make sure that you understand something is to see if you can explain it to other people. If the amount of articles I found explaining the concept of closures is any indication, it seems like there are a lot of people in the same boat as me trying to understand this tricky concept. So if you're still here and are also in that boat, read ahead and I hope my dive into the subject is as helpful to you as the articles that helped me wrap my head around it.

So what is a Closure?

I think the first step to actually understanding something is taking a look at it's definition and seeing what we can gleam from that. Let's take a quick look at the MDN documentation and see what it has to say.

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. In JavaScript, closures are created every time a function is created, at function creation time.

So we can gleam from this definition that a closure is something that is created every time a function is created, and so every function has one. It has something to do with the scope that the function is created in, and it gives you access to it. I think the next step to understand what is going on here is to talk a little bit about scope and lexical environment.

It's all about context

What does it mean when we talk about the environment or scope in which code is executed in JavaScript?

When you start a program, you start in the global execution context. Anything that you define within this context is considered to be in global scope. These are your global variables.

Now something special happens when you begin to call functions inside the global context and in some cases, inside other functions. We know that these functions have access to all of the variables within the global scope, but not the other way around. That is because when a function is called, JavaScript creates a new local execution context specific to that function and throws it onto the execution stack. When JavaScript looks for a variable, it will first look in the current execution context and then move all the way through to stack to the global context until it finds what it is looking for. If it goes all the way to global and still can't find the variable, it will return undefined.

Let's look at some code


let four = 4;

function timesTwo(x) {
   let times = x * 2;
   return times;
}

let num = timesTwo(four);

We can see a much more concrete example of this context in the code above. There's no closures here yet, but this foundation is the most important part to understanding them.

Above there is a variable four which is in the global context.

We also have a function definition of timesTwo which is also within the global context.

The last little bit of code here is the key, where the timesTwo function is called using () and passing in four as an argument.

When that function is called, we are no longer inside the global context. JavaScript creates a new local context for the function. The variable x is assigned to the value that was passed as an argument, and the variable times is assigned to that value multiplied by two. The important thing to note here is that these variables only exist inside the local context of the function. When we hit the return statement that local context is destroyed, along with the variables it contains. The variable num is then assigned to the value that was returned by the function call, and we are back in the global context.

Can we talk about closure now?

Ok, I think it's time we can finally look at what closure looks like. Actually, I lied a little bit. The example function above does have a closure, but since global context is available to all of the code inside of it, closure isn't really useful or relevant there. In order to get a better example, we need to take a look at functions returned by other functions.


function makeCounter(){
   let counter = 0;
   return function () {
      counter = counter + 1;
      return counter
   };
}

const addOne = makeCounter();
const a = addOne();
const b = addOne();
const c = addOne();
console.log(a, b, c)

Now if you take a look at the code above and have been following along, you might notice something seems a bit off. Following the logic thus far, the counter variable that is defined in the local context of the makeCounter function would be destroyed before the function it returns makes use of it. You would then expect that counter = counter + 1 would evaluate to 1 every time, since undefined + 1 would return a value of 1. Try and run the code and see what happens.

The console logs 1, 2, 3, what the heck? That's a closure at work! Think of the closure as a little backpack. When a function is created, it not only creates a local context, it also creates a closure. This is a little backpack that bundles up everything that function has access to in its outer scope when it is created. In our case this includes the counter variable that was defined in the outer function. So even though that outer context and variable were technically destroyed when our program exits the outer function, we are able to maintain access to it through the closure that was created.

You can tell by the way it is

And so that's basically closures. The easiest analogy that I have seen across many explanations is that of a backpack. Every function gets a little backpack that is created with it that it carries around with references to all the data it had access to in its outer scope. I hope this little article is as helpful at helping you understand closure as it was cementing that knowledge for myself. See below for some more in depth information that I used to help understand it better.

Discussion (1)

pic
Editor guide
Collapse
wulymammoth profile image
David

A popular interview question that I used to ask:

What happens here?

for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}