What is a closure?
In order to really understand closures, it is helpful to know how scope works. We covered scope in a previous article, so we’ll quote it here:
“If you declare a function within another function, you create what’s called a scope chain.
The outer parent cannot see the variables of its child(ren), but the child can see the variables of its parent(s). Confused? Think of a Matryoshka doll. Now imagine if these dolls were given life(scary thought). An outer doll wouldn’t be able to see the gruesome innards of an inner doll. The inner doll can see everything. Now that’s scope.”
We can conclude that the inner function has three scope chains
The first chain allows the inner function to access its own variables. The second allows it to access the variables and parameters of the outer function. And the third allows it to access the global variables. Every time we add a function, we’re adding a new link to the chain.
By defining a function within another function, we create a closure. Closures themselves can also take on the role of outer functions. Take this somewhat practical example of multiple closures:
In this example, we have two closures. combineName() is a closure to the outer function of sayMyName(). getRandomInt() serves as both a closure to combineName() and sayMyName(). What this means is that combineName() has three scope chains while getRandomInt() has four scope chains. Every time we create a closure, one link is added to the scope chain. So, getRandomInt() can access the variables of combineName() and sayMyName().
Side Note: As you can see from the example, we were able to “borrow” the parameters from sayMyName and use them in our first closure, all thanks to the trickle down effect scope chains have, which, *ahem*, is far more reliable than trickle down economics. One point to note, however, is that you cannot call an outer function’s arguments object from within a closure.
Closures In Depth
We’ve briefly outlined what closures are. Now let’s go through some use cases and explore the deepest depths of closures.
Closures are basically the walking dead.
When a function returns, the idea is that all of the variables within that function should be non existent. Yet, closures still have access to any variables in their scope chains.
Here’s an example:
The reason why we can still access the name of this “zombie” isn’t due to any sci-fi magic, it’s because closures don’t store value. Values are static. Instead, they store references to values in their scope chains. The amazing thing is that if you were to change the value of a variable up in the scope chain before returning the closure, the same value used within the closure will then update. This phenomenon is called state, which is the backbone of programming. We want to be able to create functions whose inner data lives on through the lifetime of our apps. When a user inputs data, we want that data to be updated.
We’ll show an example of how we can use closures to create a modular pattern by modifying our above code:
What we’ve created is an anonymous closure by enclosing our first function within parenthesis.
Any function or variable within our anonymous closure is considered private. The closures within the anonymous wrapper after the return statement have access to the private functions, but users cannot access the private functions outside of the module. The private data is effectively closed off by our anonymous closure, as most private things are.