(This article is part of an ongoing series on soft skills and technical wizardry from Nathan Thomas, a software engineer working in San Francisco. Click here for the previous article in the series, "Building Your First GraphQL Server." Click here for the next article in the series, a piece called "Building Your First GraphQL Server.") Starting From the Outside In Explaining closures is a frequently-used JavaScript interview question, but many engineers still don’t fully understand how they work. There’s a fog that surrounds it, much like the feeling you get when you eat too much during the holidays. 🥧 When I first started learning closures, my first impulse was to close my laptop screen and go stare existentially at the local pond. But don’t worry; closures trip up a lot of people, but you’ll understand how they work and the concepts behind them by the end of this article. I’m going to walk you through a really cool (and more importantly, ) example. You'll get it in no time at all. simple Grab your favorite coffee, apple cider, hot chocolate, or wintery beverage, and let’s get started. ☕️ "I'll use any excuse to buy a new backpack." - Michael Potts Grab Your Backpack Closures are an interesting concept. They are truly invisible in your code, and yet they are very real. If you've written any JavaScript, you've almost certainly used them without even realizing it. If nothing else, that should give you comfort; if you're already accidentally using them successfully, half the battle will just be learning the ideas behind them to fill in the gaps of how they work. At a very base level, a closure is an invisible compartmentalization of variables that are accessible to your code at various levels (e.g. the global scope, local function scope, etc.). Each time you write a function, a closure is created for the scope available to it. JavaScript does this automatically, so all we need to do is understand it. For instance, let's say we have the following code: globalVariable = ; { localVariable = ; { globalVariable + localVariable; } } closure = createClosure(); .log(closure()); const 1 ( ) function createClosure const 2 return ( ) function return const console // returns 3 This code will log the number to the console. JavaScript has created multiple scopes for your code behind the scenes; there is a global scope with the in it and local scopes with the accessible in them. 3 globalVariable localVariable and globalVariable Here's the same code with comments included to indicate which parts are global and local scopes: globalVariable = ; { localVariable = ; { globalVariable + localVariable; } } closure = createClosure(); .log(closure()); // global scope with globalVariable available const 1 ( ) function createClosure // local scope with localVariable and globalVariable available const 2 return ( ) function // local scope with localVariable and globalVariable available return const console // returns 3 Stop and think about what's happening here for a moment; the createClosure function can access both variables. What if I told you that this ability of closures to access all variables in scope at time of the function's creation could be used later when the function is used elsewhere, even another file? I know. Has science gone too far? Let's find out. 🧪 “ A good traveler has no fixed plans and is not intent on arriving." - Lao Tzu Counting with Closures Here's the code we're going to be working through for the rest of the article. Go ahead and read through it. 👍 Don't worry if you don't understand how it relates to closures yet. Just get an idea of what code is actually doing: { ( startingNum !== ) ; num = startingNum; { num++; num; } { num--; num; } { num; } [increment, decrement, getNum]; } ( ) function createCountingClosure startingNum if typeof "number" return null let ( ) function increment return ( ) function decrement return ( ) function getNum return return When we invoke that function, here's what we get out of it: [increment, decrement, getNum] = createCountingClosure( ); const 0 Furthermore, we can invoke those , , and functions to get the following: increment decrement getNum increment(); increment(); increment(); decrement(); increment(); getNum(); // 1 // 2 // 3 // 2 // 3 // 3 Whoa. Don't worry. We're about to work through this code in detail to figure out exactly what's going on. (Side note - We're doing some destructuring here. If you don't know what destructuring is, no worries. Check out MDN's excellent article on it .) here Let's look at that function again and break down each part: createCountingClosure { ( startingNum !== ) ; num = startingNum; { num++; num; } { num--; num; } { num; } [increment, decrement, getNum]; } ( ) function createCountingClosure startingNum if typeof "number" return null let ( ) function increment return ( ) function decrement return ( ) function getNum return return First, we're taking in an argument called . We are clearly expecting it to be a number (as the next line, an if statement, returns if it isn't). startingNum null Next, we're assigning the value of to the variable. This gives our counter function a starting value to work with and change later. startingNum num We then proceed to declare three functions: , which increases the value of and returns its value increment num , which decreases the value of and returns its value decrement num , which returns the current value of getNum num Finally, we return these three functions inside an array. As functions are "first class citizens" in JavaScript, we can pass functions around like this to be returned from our function and used elsewhere. This is why, later on, we can invoke createCountingClosure and destructure out these three functions: [increment, decrement, getNum] = createCountingClosure( ); const 0 As you can see, we're also passing into our function. Remember how we set it up to accept a argument? That value is going to be in this example. 0 startingNum 0 With all of that out of the way, we're now at the part of discussing the function where we can talk about how the closures work. createCountingClosure Hold on to your butts. What's in the Bag? Let's take a look at the function again: createCountingClosure { ( startingNum !== ) ; num = startingNum; { num++; num; } { num--; num; } { num; } [increment, decrement, getNum]; } ( ) function createCountingClosure startingNum if typeof "number" return null let ( ) function increment return ( ) function decrement return ( ) function getNum return return When we declared , , and , we unknowingly created little closure "backpacks" for each of them that store access to the all variables available in their scope when these functions were created. increment decrement getNum Remember at the start of this article how we had global and local scope? Remember how the function we created had a local scope that retained access to the in the global scope? globalVariable Well, the same thing is happening here. For instances, the function retains access to the variable. In fact, all of these functions can still access this variable later on anywhere that we might use them (even if it's in a different file). increment num For example, remember how we destructured out all of these functions when we invoked ? createCountingClosure [increment, decrement, getNum] = createCountingClosure( ); const 0 Here's what the function has access to (in code) at the time it's destructured out as we did above: increment num = startingNum; { num++; num; } let ( ) function increment return Even though we've only destructured out , it still has access to the variable. It's in the function's closure backpack, along for the ride like a tiny little Yoda. increment num What this means is that we can access and our variable even though we don't have it directly inside the function itself. modify num Isn't that wild? 🦁 This same thing is true for and , too. They both have access to in the same manner through their closure from when they were created. decrement getNum num Let's go ahead and use our code to see how this works. "I would gladly live out of a backpack if it meant I could see the world." - Unknown Updating Your Backpack We're going to use our code and walk through it step by step to observe what it does. Here's a refresh of the entire thing before we get started: { ( startingNum !== ) ; num = startingNum; { num++; num; } { num--; num; } { num; } [increment, decrement, getNum]; } ( ) function createCountingClosure startingNum if typeof "number" return null let ( ) function increment return ( ) function decrement return ( ) function getNum return return In addition, we're going to invoke our function and destructure out the functions returned in the array like we did previously: createCountingClosures [increment, decrement, getNum] = createCountingClosure( ); const 0 With this setup, we're now ready to go. If you want to play around with this while we walk through the rest of this article, here's a that will allow you to interact with the code. codepen link First off, we're going to invoke the increment function: increment(); // should return 1 As we've previously discussed, the function retains access to the num variable. When we invoke , the function will increase the value of behind the scenes. increment increment num Since we passed in the value of when we initially invoked up above, we increment that value to . 0 createCountingClosures 1 (If you're following along in the CodePen I provided, you'll see that pop up in the console because I've wrapped the function call in a .) console.log Next, we'll invoke increment two more times like so: increment(); increment(); // should return 2 // should return 3 Notice that the value being returned each time is not starting at ; this is because the same variable that we used above is also retained by and modified each time it is invoked. This is the incredible power of closures! 1 num increment Next, we're going to call the function to decrease the value of : decrement num decrement(); // should return 2 Notice that the value is, once again, not starting at . The functions and both retain access to the same variable and share the ability to modify it even though it is not contained inside either of them. 0 decrement increment num Finally, we're going to call one more time and cap it off by calling the function (which just returns the current integer value of ): increment getNum num increment(); getNum(); // should return 3 // 3 All of these function calls have access to the num variable as all of them have the reference to it stored away in their little scope "backpack." The final call returns 3, which is the value of from all of the many changes we've made to it. Even though is not contained directly inside any of the functions we've been invoking, we've modified it through the power of closures. getNum num num If you're still having some trouble grasping how closures work, here's an excellent short video from MPJ (who runs the Fun Fun Function channel on YouTube) about how closures work: Conclusion I hope that this brief overlook of how closures work helps you grasp them. By imagining them as little backpacks that functions can wear as they move around your application, you can build a framework in your mind for how you can access and manipulate variables contained inside of closures. Also, now that we've stepped through how they work, you can be confident in your closure knowledge and crush your next job interview. Thanks for reading. 🔥 Nathan ( Twitter , LinkedIn , GitHub , and Portfolio Site )