Learning JavaScript Closures through the Laws of Karma

Karma — by Alan Eng

Understanding closures and being able to use them as your second nature is an important first step towards becoming an expert JavaScript developer. Unfortunately, closures are also confusing to understand, especially for beginners. In this tutorial, we will learn them in a fun way and see some practical applications beyond academic theory. You don’t have to believe in the Laws of Karma to learn Closures :)

When I do interviews for JavaScript developers, my first question is around how they understand and use a closure. If they do not answer this question, I usually start talking about anything but JavaScript. We will see that question and solve it by the end of this tutorial.

Before we understand closures, we need to learn a few other fundamental aspects of JavaScript functions, and quickly learn how scope works in JavaScript.

By first-class, we mean functions are like any other data types in JavaScript. Ignoring some nuances for simplicity, functions are like any other types in the language like strings, numbers, or objects. Functions have this special behavior by which we can call them and execute the code that they contain. Beyond that callable behavior, they can be used in almost every place where you can use an object. (Though it’s not a good practice to use a function where you need is an object.)

In this code snippet, we have defined a plain function sayHello in lines 1–3. On line 5, we are using it to show an alert message. This is as simple as functions can get in JavaScript.

But the most interesting thing about the above simple snippet is on line 7. We have declared a new variable sayHelloAgain and assigned sayHello to it. Please notice the absence of “()” after sayHello. It’s almost(there is a nuance, but you can ignore it for now) similar to writing something like:

var a = 10;
var b = a;

The important takeaway here is that both sayHello and sayHelloAgain point to the same function definition in the memory. JavaScript engine has created only one function definition.

In other words, both sayHello and sayHelloAgain are just variables which point to the same memory location that contains our function definition/value.

As trivial as this may seem, it’s fundamental to understanding a lot of complex patterns that use functions, closures, and many other advanced coding and design patterns in JavaScript.

Since JavaScript functions are first-class and behave like other types in the language, we can do the following things with them:

  1. Assign them to variables. (as we have seen above)
  2. Pass them to other functions.
  3. Return them from functions, as they are just like any other values.
  4. Functions can be nested inside other functions.
  5. Assign them to object properties. (We will see an example at the end of this tutorial.)

Key takeaways from the above code:

  1. Line 8 uses a function reference of add and assigns it to another variable addBorrowed.
  2. Line 10 calls the borrowed function reference and runs it. It works because addBorrowed is simply a reference to the original add function.
  3. Lines 17–19 declare a doStuff function that takes another function as its argument. Line 18 executes whatever function that is going to be passed in. This is a very simple higher-order JavaScript example.
  4. Line 23 calls doStuff with sayHi function’s reference. Please notice the absence of “()” after sayHi. We are not executing sayHi, but rather passing a sayHi function’s value as a reference to doStuff function. So now doStuff indirectly executes sayHi function on line 18 by calling functionReference(). In this case, functionReference variable is basically a reference to the same function that sayHi points to in the memory.
  5. Lines 26–28 define a function that returns another function’s reference on line 27.
  6. On line 31, we call getSayHiFunction()(), which is almost equivalent to calling sayHi(). That’s because getSayHiFunction() only returns the function that’s referenced by sayHi. Please notice the double invocation “()()”.

Understanding the above snippet is fundamental to becoming an expert JavaScript developer. As simple as the code may seem, knowing and being able to use functions as first-class values/references is the first baby step on the path to becoming a JavaScript ninja.

Covering JavaScript’s scope in entirety needs a book. For this tutorial, please note that any variables declared inside a JavaScript function using var declaration aren’t accessible outside that function. They act as local variables inside that function.

function test() {
var message = "Hello JavaScript!";
test(); // shows alert
// You can't directly access "message" variable that's declared inside the test function from outside the test function

You can also nest functions inside functions, and scope rules often work intuitively.

When functions are nested, inner functions can access outer functions’ variables. This snippet shows a very simple example. There are some nuances like shadowing and etc that we will ignore to simplify this tutorial.

On line 10, inner function is not only able to access sum, but also it is able to update sum variable that’s declared in the outer function. On line 13, we call inner function. And when we alert sum on line 14, we see 30 as it was updated by inner function call.

Think of Russian Nesting Matryoshka dolls for a metaphor. Inner dolls know everything about all the outer dolls.

Now that you have learned how functions in JavaScript are much more dynamic when compared to methods in Java, I know you want to see a closure now, or I am going to get some bad karma if I delay it any further!

Let’s see a closure first and work our way down to understand it, and then finally build a real-world example towards the end. (Btw, that’s my interview question!)

Key things to understand about the above snippet:

  1. getNewLife is a function that has a nested nextLife function defined inside it. This is function nesting at the basic most level as we have already learned.
  2. On line 2, we create a message variable in the scope of getNewLife function. We calculate and store a string value in it.
  3. Lines 5–8 define a nested function named nextLife.
  4. On line 7, please notice the usage of message variable which is actually declared in the outer function.
  5. The most important thing is happening on line 11. We return the inner function’s reference through nextLife.
  6. Line 14, myNextLife becomes a function now as that’s what getNewLife returns when it’s done running.
  7. On line 17, when you execute myNextLife(), it’s able to show the message that you calculated on line 2. But the important thing here is that the message is declared in the getNewLife function which is already executed(died).
  8. Ideally if there were no closures in JavaScript, accessing message in the inner function should have thrown a ReferenceError as it doesn’t exist in that function’s scope anymore or anywhere else in the global scope.
  9. On line 20, we get another life myOtherLife by calling getNewLife again.

That’s a lot of things to digest. Take some time and read through the code and explanation again. You can copy and run the code as well.

I can guess what you are probably thinking: But show me the closure here? Where the hell is it hiding? Is it even there? Or is it my bad karma that I am reading your tutorial?

Good question. Thanks for being patient. This takes us to one of the fundamental principles about closures that most books and tutorials do not explicitly highlight.

Closure is not a tangible thing in JavaScript. By that I mean: it’s not like a variable holding a value, neither is it a function. And more importantly, neither is it an object.

So what is it then?

A closure is a name given to a feature in the language by which a nested function executed after the execution of the outer function can still access outer function’s scope.

Well let’s map this cryptic definition to our code and the laws of karma. In our code on line 14, getNewLife function is executed and returned. The return value is a function. That’s the trick here. That’s where a magical closure of scope can be observed. Now the returned function, which is nextLife in our case, is assigned to a variable named myNextLife. Now on line 17 when we execute myNextLife(), the scope of outer function should no longer be accessible if it’s not for closures. Thank god, JavaScript has some concept of Karmic Philosophy around functions.

myNextLife which is a reference to the function that got created when we called getNewLife is able to access message variable and its value even after getNewLife has finished running. That’s my friends, is an observable closure for you.

Now with this understanding let’s try to define a closure again:

For a closure to be observed, we somehow need to have a reference to an inner nested function defined in an outer function. The inner function can still access the scope of outer function even after the outer function has finished executing through the closure mechanism.

In terms of Karmic Laws, even after someone dies, the previous life’s karma influences the next life.

Previous life = Outer function
Next life = Inner function
Next life can access previous life’s karma score even after death.

That’s a lot of JavaScript and life in general.

Let’s see a real world practical example with closures now.

Remember my mention about the first interview question?

In programming, we often need a sequence generator which generates strings like S1, S2, S3 and so on. Let’s try to solve it with brute force way and see what are the problems with it.

The above code works very well. But we are using global state for storing current value, which is shared across all the other code in the application. Anyone can accidentally or intentionally(bad karma for them) change the value of current to anything. That breaks all the consumers of your getSequenceValue function. Which, of course, can give you a lot of bad karma!

Luckily, we can exploit closures to solve the sequence problem by using private state instead of shared global state. After the following example, we will finally see how to use Immediately Invoked Function Expression to improve the following code.

Key takeaways from the above snippet:

  1. No one can accidentally or intentionally mess up the sequence value and there by messing up some other application logic.
  2. Lines 1–11 define a temporary function tempSequenceMaker. It’s like our nextLife function. It basically returns another function declared inside it. This happens on line 10.
  3. On line 13 we store the returned function reference into our own variable getSequenceValue. Then on line 17, as we no longer need tempSequenceMaker, so we null it out. (We will see a better pattern to avoid this temporary function definition.)
  4. Line 19, 20 use getSequenceValue reference as a function and get the expected return value for the sequence every time it’s called.
  5. The current variable is now private and has a life inside the intangible closure through the nested function that was returned when the outer function finished running. Only code that can access it is the sequence function in the memory somewhere.

Now that’s so cool. That’s my friends, is the answer to my first interview question. They should at least solve it this way to talk more about JavaScript.

Now let’s see how to avoid that temporary function definition.

Immediately Invoked Function Expression to the rescue:

First let’s learn what are functions expressions. In simple terms, a function expression is any function definition/declaration that doesn’t start with the keyword function. Let’s see some examples to learn what are they.

These are some examples of using functions as expressions. The most interesting of these is on line 6. We put a function inside parenthesis like:
( your function here ).

Now when we add one more “()” at the end of that expression above, and that’s my friends, is an Immediately Invoked Function Expression or IIFE as it’s lovingly called in the circles of JavaScript maniacs, sorry I meant geeks :) There are other stylistic variations. I will write a post on this topic separately.

So the above snippet executes the code inside the function immediately. If IIFEs could only do this, there isn’t a lot of fun. The most important thing about an IIFE is that it can return a value/reference and you can assign that to a variable.

So now that we are equipped with the understanding of Laws of Karma for Closures and IIFE, let’s solve our interview question in the right way.

Key takeaways:

  1. An IIFE can return a value/reference and we can assign it to a variable.
  2. IIFE effectively creates a private scope.
  3. No extra global variables are created beyond sequence.
  4. No code can mess up the sequence value either intentionally or otherwise.

Some karmic Closure-ly recap on what we have learned so far:

  1. Closures aren’t a tangible thing like objects, functions, variables.
  2. A closure is formed when we nest function, and a closure is observed when the nester function runs after the outer function has finished running.
  3. Nested functions can carry the bag of karma from all the outer functions even after the outer functions died(finished running).
  4. Not sure if you have noticed, but the karma bag is a living thing. You can change the karma bag(variables) in the next life. Notice how the counter gets incremented every time you call the sequence function.
  5. If there are multiple nested functions that get exported at the same time, all those functions share the same karma bag. This is a bit advanced, but the following example shows a quick demo.

This is an advanced example as you need to understand objects as well. But you get the idea, right! By the way, this is a variation on the world famous Module Pattern in JavaScript.

(Please note that I have intentionally left the explanation around the internals of execution context, and other nitty-gritty details for this introductory tutorial.)

Congratulations! You are now a certified Karmic Expert in JavaScript Closures.

Thank you for reading this tutorial. Please share your feedback in the comments below. I would love to know how this tutorial has helped you learn closures, and if there is anything I can do to make this better.

If you liked reading this post, you can show some love by clicking on 💚 :) and recommending it to others.

For my day job @Salesforce, I write code for machines; But in my free time, I pursue this wild hobby of writing for humans. {Learn | Code | Read | Write}

For my day job @Salesforce, I write code for machines; But in my free time, I pursue this wild hobby of writing for humans. {Learn | Code | Read | Write}