Getting “Closure” on closures
Suppose you bought a brand new muscle car. You were super excited to get it and you’ve been driving it around town a lot. You can get from point A to point B and then all the way to point C. It’s a great car! However, there are features of your car that you probably don’t even know yet, features that once you understand will not only allow you to get the best performance out of your car but to have a deeper appreciation for the car and it’s makers.
What is an Execution context?
Before we dive into closures we need to understand something called the execution context.
There are three types of execution contexts. Global and function and eval (eval isn’t covered here).
Global Execution Context
Function Execution Context
As the flow of execution continues in Global it may encounter a function call. Once it sees that it creates a new Function Execution Context. Each function execution context creates its own local memory (variable environment or state). However, once a function has finished running and returns an output the local memory (state or variables) gets deleted, so functions have no permanent memory by default.
If you want even more info on this look at this article here
However, what if we could give our functions a memory? What if we could allow them to carry a permanent local memory (state) with them and have it with them for the life?
What is a Closure?
So…. what is a closure? Many will tell you it’s the stuff in side the curly braces of a function. But does that really explain what it is? Not really. To get a good understanding let’s take a look at some code.
What’s happening here?
We have a function named outer. This function contains a variable named counter with the value of 0. Next we have a function declaration named incrementCounter which increases the counter variable. We are then returning the function incrementCounter. Then we are declaring a constant with the name “myNewFn” and giving it the returned value of our outer function.
So, what’s so special about this. Let’s see what happens when we call “myNewFn()”. Copy the code and run it in a browser and see what happens. I’ll wait…..
To illustrate, imagine for a moment that whatever house you were born in you got to keep certain things in the house for the rest of your life. No matter where you go until you die those certain belongings from that house are yours and you can use them anytime you like wherever you like. This is in essence what a closure is.
There’s also another name for this, a P.L.S.R.D or Permanent Lexical Scoped Referenced Data. But that’s a mouthful, I like backpack much better.
Now let’s change things up a bit. What if we create a new constant named secondFn and gave it the return value after the function outer has run. Then we call secondFn twice. What does it evaluate out to be? Go ahead and take the code and run it right quick.
const secondFn = outer()secondFn()secondFn()
If you got “1” then “2”, you are correct. But why? Well, that’s the beauty of the backpack. Every time you call function outer and stuff it in a new constant it takes with it a brand new backpack with a totally separate counter variable which is set to zero.
What if we put the counter variable inside the IncrementCounter function and run it. All of them would evaluate to 1.
Now, let’s move the counter variable outside both functions to the global state. Now it’s 1 2 3.
Why does this happen? Since the variable is in the global scope all the functions pull from the same variable and iterates the counter.
What if we wanted to create a function that could only be executed once and when it’s run again it would just return the same first result each time. This could be useful in situations where a feature in an application should only be used one time by the user and subsequent calls should return the same value. Let’s see how we could implement that using closures.
Let’s break this function down. First, we are creating a function called once that takes a function as a parameter. This function has a variable called “result” which is uninitialized. Next we are returning a function that will take a number as a number as a parameter. Then we are calling the function with the parameter and storing the results in our variable result. We need make our function parameter equal to null so it can’t be called again. Finally we return the result.
The interesting thing about this function is that when it’s returned out it grabs the result and stores it in its backpack even though it hasn’t been assigned yet. Because it has access to that result variable it knows when the function has been called and will not let it be called again.
Let’s try the function out with an function that adds two to a number. Copy and paste the code in a browser and see if you get the expected results.
Pretty cool huh? This function is also an example of currying but that’s a topic for another article. Feel free to play around with this function and make it your own.
So to recap a closure is a backpack full of permanent data that the function takes with itself when it’s created and returned from another function. It can then use this data as many times as it needs and whenever and wherever it is called for the life of the function.
Closures give our functions persistent memories.
If you enjoyed this article please give it a like. Also, feel free to check out my other articles here.