Tarandeep Singh
Tarandeep Singh

Follow

Tarandeep Singh

Follow

Deciphering Closures in JS 🔥

Mystery Behind The Scenes

Tarandeep Singh's photo
Tarandeep Singh
·May 11, 2022·

5 min read

Deciphering Closures in JS 🔥

Table of contents

  • Prerequisites 🎋
  • What are closures? 🕵🏼‍♂️
  • Lexical Environment ✨
  • Exposing Closures 📦
  • Power of Closures 💪🏼
  • Conclusion ✏️
  • References 🧾

Let’s be honest, we all have scratched our heads when we first read about Closures. The whole concept around closures does seem a little complex at the beginning, which is exactly why I am writing this blog!

So rest assured guys, after reading this blog, you’ll never get confused again when someone asks you about “closures” in an interview!

If you have been writing JavaScript for a while there’s a high chance you have already encountered many closures, although you might have not noticed them.

So let’s dive right in!

Prerequisites 🎋

Although I have tried my best to make this blog as beginner-friendly as possible, still it would be good to have some knowledge of basic JavaScript concepts, how block scopes and function scopes work, the difference in scopes of var, let, and const.

What are closures? 🕵🏼‍♂️

A function bundled together with reference to its lexical environment is known as closure.

I know I know, no one in the history of mankind has ever understood this bookish definition at once. I have noticed more eyebrows getting raised at the word “lexical environment” than any other thing. So let’s break it down:

  • One thing that is clear from this definition, a function combined with “something” forms a closure.
  • Now this “something” disturbs a lot of programmers. The infamous “lexical environment”.

Lexical Environment ✨

In theory, the word “lexical” just means “in order” or “in a hierarchy”. In terms of code, it refers to the place where a specific code is present physically inside the code.

For instance, have a look at this code snippet:

1. function outerFunction() {
2.      var a = 10;
3.     function innerFunction() {
4.         console.log(a);
5.     }
6.     innerFunction();
7. }
8. outerFunction();  // output: 10

Here, the var a = 10; is inside the lexical scope of innerFunction() . But how is this possible? Isn’t the scope of inner function only from line no. 3 to 5? Yes, that’s true. Then how does inner function has access to a at line no. 2? That’s because we are not talking about the scope here..... we are talking about the “lexical scope”, which simply means the scope of a function’s parent. Yup, it’s that simple!

When the program reaches line no. 4, it first checks for the variable a in the scope of inner function but when it couldn’t find a there, it checks for the lexical scope of innerFunction() which refers to? Yes, the scope of outerFunction() ( from line no. 2 to 7 ).

The existence of innerFunction() and its lexical scope is known as the Lexical Environment.

All functions remember the lexical environment in which they were made. Alright, I’ll reveal the whole magic to you.🎩 All functions have a hidden property named [[Environment]] , that keeps the reference to the lexical environment where the function was created. Now you know the secret, don’t tell anyone! 🤫

So, innerFunction().[[Environment]] has the reference to {a: 10} Lexical Environment. That’s how the function remembers where it was created, no matter where it’s called. The [[Environment]] reference is set once and forever at function creation time.

Exposing Closures 📦

In simple words,

Closures give us access to an outer function’s scope from an inner function.

Honestly, I could have given this simpler definition at the very beginning, but then you would have never understood what actually goes behind the doors? How does a closure actually give us access? What in the world is the “Lexical Environment”? Now that you know all this, the above definition would be much easier to digest! 😋

Power of Closures 💪🏼

There are many practical reasons to use closures, but the one that stands out is Data Encapsulation. This is required in order to prevent leaking or exposing data where it’s not needed. Other uses include: Currying, setTimeouts, maintaining state in async programs, etc.

Let’s have a look at one last example:

1. function outerFunction() {
2.      var a = 10;
3.     function innerFunction() {
4.         console.log(a);
5.     }
6.     return innerFunction;
7. }
8. var x = outerFunction();
9. // 1000 Lines Of Code
1009. x();  // output: 10

This might look similar to the first example, but on a deeper glance, you’ll notice some changes! Here, we are just returning the innerFunction() rather than calling it then and there.

Now, let’s imagine there are 1000 lines of code after line no. 8, of course, you’ll have to imagine I am not gonna write 1000 lines just for the example.

Now, one might argue that once line no. 8 gets executed, the outer function gets called and is removed from the call stack afterward. By the time the program reaches line no. 1009 the outer function is long gone, along with var a at line no. 2. So how is the output still 10?

Yup! you guessed it right, it’s the closure that was formed between innerFunction() and its lexical environment (where a was present) at the time of function creation.

Conclusion ✏️

  • Closures give us access to an outer function’s scope from an inner function.
  • A lexical Environment is nothing but an environment where a function exists along with its parent’s scope.
  • Closures are used in many places like Data Encapsulation, Currying, etc.

I believe that now you would absolutely nail your answer in an interview where closure is asked! Do let me know your favorite part of this blog in the comments.

You can follow me on Twitter and LinkedIn

References 🧾

 
Share this