Closures and their power in JavaScript

Closures and their power in JavaScript

Table of contents

No heading

No headings in the article.

We all must have heard of the term Closures in JavaScript and some of you must have encountered it during a tech interview.

In this article, I will explain to you what exactly Closures are and how powerful they are if you use them properly.

Let's get started with the simple definition that MDN has provided us,

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

This is not much of help for someone new to closure or JavaScript. As you can see, MDN mentions Lexical Environment here. What exactly is the lexical environment?

Lexical environment or Lexical scope, simply put Lexical scope is where you write something inside your code. Does that make sense? Let me try to explain it a little bit better. As you know that our JavaScript engine looks and reads through our code so it's important where we write something. So when we create a function, that function has its lexical environment.

Simply put, the JS engine knows, based on where our code is written before we even run the code, what variables each function has access to. This is what lexical environment is.

Closure is a feature of JavaScript. Closure allows a function to access variables from an enclosing scope or environment even after it leaves the scope in which it was declared.

Let's get into some coding and understand what closure is in JavaScript

function a() {
    var name = "John";
    return function b() {
        var age = 20;
        return `I am ${name}.`
    }
}

a()();

Now, look at the above code. Can you guess what will be the output? The output will be I am John.

You guys might have guessed it correctly because that is the expected behavior. But how did the inner function remembers the outer variable? Because of closure. Yes closure is at work here that is why we got the response I am John.

Let me explain to you why did we get what we get.

So when we ran the function `a()`, we got another function in return. And when you return something from a function that means the execution of that function is finished and that function is removed from the memory. Then how, inside function b(), do we get access to the name variable? Because we know that every function has its execution context and variables, when the function is popped off from the memory after execution then the variables are also popped off. But then how do we still have access to the name variable inside function b()? Because of closure.

So JavaScript sees while executing function a() that the local variable is being referenced inside another function so then it puts that variable into a box called closure. We can call the place a box called closure. Then JavaScript will get on to the returned function and while executing it will have access to the variable because it's still in the box called closure. Once it's finished and sees that now the variable is not being referenced anywhere then it will pop that variable off the memory.

So until now, you might have gotten the idea of what closure is in JavaScript. But why is it important, and how is this feature so useful?

Let's understand how powerful closures are in Javascript with an example.

So we have a function that is doing some heavy-duty calculations.

const heavyDuty = (index) => {
  const arr = new Array(7000).fill("0")
  return arr[index]
}

heavyDuty(10);
// It will return 11th element of the array

This is a simple function but assumes that it's a very heavy-duty function that is doing some complex calculations. So whenever we run this function a new memory space will be created for the huge array.

const heavyDuty = (index) => {
  const arr = new Array(7000).fill("0")
  console.log("Ran!")
  return arr[index]
}

heavyDuty(10);
heavyDuty(20);
heavyDuty(30);
heavyDuty(30);

Output in console:
Ran!
Ran!
Ran!
Ran!

You can see that every time the array is created. This is a simple example but what if we have a function that takes a lot of space and does some heavy calculations, then we'll not want that function to run like this.

We can use closure to make this function optimized. Let's see how.

const heavyDuty = () => {
    const arr = new Array(7000).fill("0")
    console.log("Ran!")
    return (index) => {
        return arr[index]
    }
}

const callHeavyDuty = heavyDuty()

callHeavyDuty(10)
callHeavyDuty(10)
callHeavyDuty(10)
callHeavyDuty(10)

Output in console: 
Ran!

Did you see the difference? This time instead of 4 times, Ran! is only logged once. How? With the help of closure. Let me explain it to you.

On const callHeavyDuty = heavyDuty() line, we are storing the returned function into a variable and then calling that variable with index as the arg. Like this, we are creating a closure inside the function and without calling the main function multiple times we are calling the returned function multiple times by storing it into a variable. This way we are not creating an array again and again i.e. recalculating the heavy calculation.

Now you have an idea of how useful and powerful closures are in JavaScript.

This was a very basic example where we took advantage of closures and made our function much more optimized. But in real applications, you might need to add a little more to it depending on what you are trying to achieve.

I hope you got the idea of what closures are. But don't just stop here, go ahead and try it with some more examples and try to understand it more in-depth.