Finding Closures

Korla
5 min readJan 3, 2021

The following post requires you to have a healthy understanding of how scope works in JavaScript.

It wasn’t a long time back that I learnt about closures in JavaScript, even though I had been working with the language for over 3 years now. Abysmal ? Yes, but now I see it. Though the onus lies with me to dive deeper into a language, it was my (mis)understanding that a lot can be done when it comes to building real world applications with JavaScript with the “good parts”. Needless to say that I was wrong, and as time went I found myself often frustrated with the nitty-gritty of the language. On a random afternoon, I finally decided to poke the bear and in a series of post I will try to paint a decent picture of the language. Let’s start with closures. A down-and-dirty definition of what we need to know to understand and recognize closures:

Closure is when a function is able to remember and access its lexical scope even when the function is executing outside its lexical scope. They are a result of writing code that relies on lexical scope. They just happen, and we are attempting to understand them, so that we can leverage them.

Let us consider the following code:

As we can see, the function bar has lexical scope access to the inner scope of foo, and we return the function object that bar references when foo is executed and assign it to baz. When we execute baz, bar is executed for sure, but its executed outside of its declared lexical scope. Under normal circumstances, once foo is executed, we would expect that the entirety of the inner scope of foo would go away as well, because the engine deploys a garbage collector that comes along and frees up memory not in use. But this is where “closures” come into effect and do not let this happen. The inner scope stays intact, used by the function bar itself. Thus, we say that the function bar has a lexical scope closure over foo, which keeps the scope alive for bar to access later. An important thing to note here is that irrespective of the method we use to transport an inner function outside of its lexical scope, it will continue to maintain a scope reference to where it was originally declared, and when executed, the closure is exercised. Yes, that is it. Let us take another example with a loop:

For this code, I would normally expect the behavior to be that the numbers 1, 2, …5 would be printed out, one at a time, every one second, as the timer closes on the scope where i exists. But in fact, if you run this code, the output will be 6, repeated 5 times, at one second interval.

Let’s explore as to why our code did not execute as expected and where does the 6 come from. The terminating condition of the loop is when i is not <= 5. The first time that occurs is when i is 6. The output is therefore reflecting this final value of the i after the loop terminates. Now when you think about it, it may seem obvious that the callbacks are all running well after the completion of the loop. Even if we set the timers to be 0 instead of i*1000, all the callbacks would still run strictly after the completion of the loop, and this prints 6 each time.

The reason our code does not behave as semantically expected is our assumption that each iteration of the loop is assigned its own copy of i at the time of the iteration. But the way scope works, all five of those functions, though they are defined separately in each loop iteration, share the same global scope, which has only one i in it. The loop structure often has us believing that something complicated is at work, when indeed, there is no difference than if each of the five of the callbacks were declared one right after the other.

Now, to fix this behavior, we can start by making sure that setTimeout() is executed immediately. This is where IIFE or Immediately Invoked Function Expression comes into the scene. Let’s update the code:

We know that IIFE creates scope by declaring a function, hence, each callback function now has it’s own scope. But does it work ? No. It is not enough to have a scope to close over if that scope is empty. If we look closely, the IIFE function is just an empty scope. It needs its own variable, with a copy of i value at each iteration. The code now looks as follows:

Using an IIFE inside each iteration created a new scope for each iteration, which gave our timeout function callbacks the opportunity to close over a new scope for each iteration. Although, it must also be noted that there is a special behavior defined for let declarations used in the head of a for loop. This behavior states that the variable will be declared not just once for the loop, but each iteration and will be initialized at each subsequent iteration with the value from the end of the previous iteration. Therefore, the following code will also give us the correct result:

Let us look at another example:

As you might recognize, this is the pattern called “module”. In this example, the `doSomething()` and `doAnother()` functions have closure over the inner scope of the module instance.

Closures almost seem mystical from distance, but are far from it. They are a standard when it comes to code written in a lexical scope, where functions are values and can be passed around. But they can be tricky, for example with loops, but at the same time they enable module patterns in their various forms.

I hope I have brought some clarity to the subject, and as an exercise, check your existing code for closures and be mindful about them in the future.

--

--

Korla

A small town boy with big dreams, who believes programming is magic. Looking for projects. JavaScript, Go and Python.