JavaScript is one of the trickiest programming languages to master. Sometimes even senior developers aren't able to predict the output of the code they wrote. One of the more confusing concepts in JavaScript is closures. Beginners usually get tripped up on the concept—don't worry. This article will slowly drive you through the basic examples to help you better understand closures. Let's get started.

What Are Closures?

A closure is a structure of a function and its lexical environment, including any variables in the function's scope at closure creation. In simpler terms, consider an outer function and an inner function. The inner function will have access to the scope of the outer function.

Before looking at some JavaScript closure examples, you'll need to understand lexical scoping.

What Is a Lexical Environment?

The lexical environment is the local memory along with its parent environment. Check out the example given below and guess the output of the following code:

        function outer(){   
 let a = 10;
 console.log(y);
 inner();
 function inner(){
   console.log(a);
   console.log(y);
 }
}
let y = 9;
outer();

The output will be 9, 10, 9. The inner function has access to the variables of its parent, the outer() function. Hence, the inner() function can access variable a. The inner() function can also access variable y because of the concept of the scope chain.

The parent of the outer function is global, and the parent of the inner() function is the outer() function. Hence, the inner() function has access to the variables of its parents. If you try to access variable a in the global scope, it will display an error. Hence, you can say that the inner() function is lexically inside the outer() function, and the lexical parent of the outer() function is global.

JavaScript Closure Examples Explained

Since you have learned about the lexical environment, you can easily guess the output of the following code:

        function a(){
  let x = 10;
  function b(){
   console.log(x);
  }
 b();
}
a();

The output is 10. While you may not guess it at first glance, this is an example of closure in JavaScript. Closures are nothing more than a function and their lexical environment.

Let's consider an example where there are three functions nested inside each other:

        function a(){    
   let x = 10;
  function b(){
     function c(){
       function d(){
         console.log(x);
       }
     d();
    }
    c();
  }
 b();
}
a();

Will it still be called a closure? The answer is: yes. Again, a closure is a function with its lexical parent. The lexical parent of function d() is c(), and due to the concept of scope chain, function d() has access to all the variables of the outer functions and the global ones.

Take a look at another interesting example:

        function x(){
   let a = 9;
   return function y(){
     console.log(a);
 }
}
let b = x();

You can return a function inside a function, assign a function to a variable, and pass a function inside a function in JavaScript. This is the beauty of the language. Can you guess what the output will be if you print variable b? It'll print function y(). The function x() returns a function y(). Hence, the variable b stores a function. Now, can you guess what'll happen if you call variable b? It prints the value of variable a: 9.

You can also achieve data hiding using closures. For a better understanding, consider an example with a button that has an id named "button" on the browser. Let's attach a click event listener to it.

Now you need to calculate the number of times the button is clicked. There are two ways of doing so.

  1. Create a global variable count and increment it onclick. But this method has a flaw. It's easy to make modifications in the global variables because they're easily accessible.
            <button id="button">Click me</button>
     <script>
       let count = 0;
       const button = document.getElementById("button");
       
       button.addEventListener("click", ()=>{
         count++;
           console.log(count);
       })
     </script>
      
  2. We can achieve data hiding by using closures. You can wrap the entire addEventListener() function inside a function. It makes a closure. And after creating a closure, you can create a count variable and increment its value onclick. By using this method, the variable will remain in the functional scope, and no modification can be made.
            <button id="button">Click me</button>
     <script>
       const button = document.getElementById("button");
       function countOnClick(){
         let count = 0;
         button.addEventListener("click", ()=>{
         count++;
           console.log(count);
       })
       }
       countOnClick();
     </script>

Related: The Hidden Hero of Websites: Understanding the DOM

Why Are Closures Important?

Closures are very important not only when it comes to JavaScript, but also in other programming languages. They're useful in a lot of scenarios where you can create variables in their private scope or combine functions, among other cases.

Consider this example of function composition:

        const multiply = (a,b) => a*b;
const multiplyBy2 = x => multiply(10, x);
console.log(multiplyBy2(9));

We can implement the same example using closures:

        const multiply = function(a){
   return function (b){
     return a*b
 }
}

const multiplyBy2 = multiply(2);
console.log(multiplyBy2(10))

Functions can use closures in the following scenarios:

  1. To implement function currying
  2. To be used for data hiding
  3. To be used with Event Listeners
  4. To be used in the setTimeout method()

You Shouldn't Use Closures Unnecessarily

It's recommended to avoid closures unless truly needed because they can bring down the performance of your app. Using closure will cost a lot of memory, and if the closures are not handled properly, it may lead to memory leaks.

Closed over variables won't be freed up by JavaScript's garbage collector. When you use variables inside closures, the memory isn't freed up by the garbage collector because the browser feels that the variables are still in use. Hence, these variables consume memory and bring down the performance of the app.

Here's an example that shows how variables inside closures won't be garbage collected.

           function f(){
     const x = 3;
     return function inner(){
       console.log(x);
     }
   }
   f()();
 

The variable x here consumes memory even though it isn't frequently used. The garbage collector won't be able to free up this memory because it's inside the closure.

JavaScript Is Endless

Mastering JavaScript is an endless task, as there are so many concepts and frameworks that aren't typically explored by experienced developers themselves. You can significantly improve your handle on JavaScript by learning the basics and practicing them frequently. Iterators and generators are some of the concepts that interviews ask during JavaScript interviews.