Advanced JavaScript: Chapter 1 - Closures in Javascript

Advanced JavaScript: Chapter 1 - Closures in Javascript

Overview ??

Closures are one of the important concepts in JavaScript and are most widely used by developers either knowingly or unknowingly. A good understanding of this concept can improve the code and will provide a better understanding of how code is working internally and that will help in finding bugs and expecting the outcome.

Scoping in javascript ??

Before understanding closure, let's understand what is scope in JavaScript. Before ES6, javascript had only two types of scoping function scope?and?global scope, which is a variable that can be either function scoped if defined inside a function or global scoped if defined outside the function.

For example, in the below code, the variable x is defined inside the if-else block but it can be accessed outside the block also because it's globally scoped.

// there's no concept of block scope before ES6

if (Math.random() > 0.5) {
? var x = 1;
} else {
? var x = 2;
}

console.log(x);  
// Output: output will be 1 or 2 based on the condition
// but it will not give any error        
A block is a pair of braces ({...}) used to group multiple statements.

In ES6, JavaScript introduced the?let?and?const?declarations, which allow us to create block-scoped variables but those remain in a temporal dead zone until the variable gets initialized.

A temporal dead zone (TDZ) is the area of a block where a variable is inaccessible until the moment the computer completely initializes it with a value.

Understanding Temporal Dead Zone by an example ??

// block starts here
{
  // name's TDZ starts here (at the beginning of this block’s local scope)

  // name's TDZ continues here
  // name's TDZ continues here

  console.log(name); // returns ReferenceError because name’s TDZ continues here

  // name's TDZ continues here
  // name's TDZ continues here

  let name = "Abhinandan Mishra"; // name's TDZ ends here

  // name’s TDZ does not exist here
  // name’s TDZ does not exist here
}
// block ends here        

A variable's temporal dead zone starts from the first line of the code and continues till the variable gets initialized.

For const variable intialization should be done at the time of decalaration.

Closure in JavaScript ????

The closure is basically some functions bundled together that have references to their surrounding state ( known as lexical environment ).

In simple words, closure provides access to variables of the outer scope's function to inner functions.

Now, while talking about the scope we shouldn't talk about the var keyword because it's globally scoped and hence can be accessed from anywhere in the code. Our main focus will be on block-scoped variables that is let or const keyword variables.

Let's understand the closure by the following example:-

function createMailTemplate(message) {
  function createMail(name) {
    const mail = `Hi ${name}, ${message}`;
    return mail;
  };

  return createMail;  // function is returned here
}

const introMail = createMailTemplate("Welcome to fullstack insights newsletter");

const thankyouMail = createMailTemplate("Thank you for visiting my blog!");

console.log(introMail("Abhinandan")); 
// Output :- Hi Abhinandan, Welcome to fullstack insights newsletter

console.log(thankyouMail("Mishra"));
// Output :- Hi Mishra, Thank you for visiting my blog!        

In the above example, we have created a function createMailTemplate, that takes a message as a parameter and returns a createMail function that takes a name as a parameter. createMail uses the message variable of the createMailTemplate function and name parameter to create the mail and return it.

Now, the message parameter that is passed in the createMailTemplate is a block-scoped variable so it should be accessed in the scope of createMailTemplate only and we know that after a function gets executed its variables get cleared from the memory.

const introMail = createMailTemplate("Welcome to fullstack insights newsletter");        

In the above line of code, the createMailTemplate function gets executed and the message variable gets cleared from the memory.

console.log(introMail("Abhinandan"));
// Output :- Hi Abhinandan, Welcome to fullstack insights newsletter        

So, Ideally, it shouldn't be accessible when we call the introMail function. But actually, it somehow retains the value of the message variable and returns the output shown above.

This behavior of retaining the value of the variable or having access to the value of variables defined in outer functions by inner functions is known as Closure.

Concept behind closure ????♂?

The basic concept behind closure in JavaScript is scope chaining. A scope chain is a chain of scopes starting from a global scope and to a particular scope.

Let's understand the scope chaining with an example:-

let scope = "global_scope";
let global_var = "defined globally";
let func_var = "func_var_defined_globally";

function func1() {
   let scope = "func1_scope";
   function func2() {
     let scope = "func2_scope";
     let global_var = "defined inside func2";

     console.log("Accessing variables in func2 scope")
     console.log("scope:", scope);
     console.log("global_var:", global_var);
     console.log("func_var:", func_var);
   }
   
    console.log("Accessing variables inside func1 scope")
    console.log("scope:", scope);
    console.log("global_var:", global_var);
   
   return func2;
}

const fn = func1();   // executed function 1
fn();                // executed func2 returned by func1        

Output :-

Accessing variables inside func1 scope
scope: func1_scope
global_var: defined globally

Accessing variables in func2 scope
scope: func2_scope
global_var: defined inside func2
func_var: func_var_defined_globally        

Step-by-step explanation:-

1. Global Variable Declarations: In JavaScript, variables can be declared with different scopes. In this example, we start by declaring several variables in the global scope:

   let scope = "global_scope";
   let global_var = "defined globally";
   let func_var = "func_var_defined globally";        

2. Function Declaration (func1): Next, we declare a function named func1. Inside func1, a new variable named scope is declared with a local scope specific to func1. Additionally, within func1, another function named func2 is defined.

3. Accessing Variables within func1:

  • When we access variables inside func1, JavaScript follows the scope-chaining principle:
  • scope within func1 has a local value of "func1_scope," so that value is printed.
  • global_var is not defined within func1, so JavaScript looks in the parent scope, which is the global scope. It finds global_var with the value "defined globally," so that value is printed.

4. Returning a Function (func2): The func1 function returns the func2 function. This is possible because JavaScript functions can be treated as first-class citizens, allowing them to be returned from other functions.

5. Execution of func2:

  • After calling func1() and storing the returned function in the fn variable, we execute fn(). This is where func2 is executed.
  • Within func2, the same scope chaining mechanism applies: scope within func2 has a local value of "func2_scope," so that value is printed.
  • global_var is not defined within func2, so JavaScript looks in the parent scope, which is the scope of func1. Since it's not found there either, it continues up the chain to the global scope, where it finds global_var with the value "defined globally" which is printed.
  • func_var is accessed inside func2. It's not defined within func2 or func1, so JavaScript looks in the parent scope, which is the global scope, and finds func_var with the value "func_var_defined globally." Consequently, the value of func_var is printed.

The JavaScript scope chain is a mechanism that allows functions to access variables not only in their own local scope but also in their parent scopes, following a hierarchical chain until a variable with the specified name is found or the global scope is reached.

Usage of closure ????♂?

There are so many use cases of closures:-

  1. It can be used for encapsulation that is binding variables and functions within a scope that will increase code modularity.
  2. By encapsulating data inside closure it becomes inaccessible from outside the function's scope and hence it increases privacy and security.
  3. Using closure a variable can be retained without defining it globally and since it's accessible inside the function's scope only, that's why it makes it memory efficient.

There can be many other uses of closure that you will encounter while building real-world applications.

That's all for the scope of this article, hope you've added some data to your knowledge scope.

Happy Coding!

Rajesh kisan Sambakwad

Swami Ramanand teerth Marathwada University Nanded

7 个月

Nice

回复
Atul Kumar

Salesforce developer intern @TheSmartBridge | 1x Salesforce Certified ? | Trailhead Ranger | Salesforce Enthusiast | 28x Superbadges | GitHub Campus Expert ??| B.Tech - ECE Final Year @MMMUT, Gorakhpur

1 年

Thanks for sharing sir! ?

Ekansh Saxena

SDE - I @Expedia Group | Ex- SDE - Intern @Juspay | Siemens Scholar - Batch 07 | ICPC Regionalist '21

1 年

Helpful, Looking forward to more such articles ?

Nidhi Pal

Specialist Programmer @ Infosys | JavaScript, Angular, .NET Core | Full Stack Developer |

1 年

Your examples made grasping the topics much easier - great job!

Nicely explained everything ????

要查看或添加评论,请登录

社区洞察

其他会员也浏览了