Closure in JavaScript

This article explains Closure in JavaScript.

Introduction

As a developer we encounter many weird behaviors when writing code. One such behavior I actually encountered when writing a JavaScript function and came across the concept called Closure in JavaScript.

I was supposed to create 3 buttons dynamically and attach the onclick event to it that will alert me to some message based on the button clicked. Before getting into my problem, let me explain something with a simple example.

<div id="pnl">
    <input type="text" id="name" />
    <input type="button" id="save" value="Save" />

</
div>
<
script type="text/javascript">
    function Employee(name) {
        var time = 5;
        function Manager() {
            alert(name + ' will attend the meeting at ' + time);
            time++;
        }
        return Manager;
    }
    var s = Employee("Pradeep");

</
script>  

Now, in the preceding JavaScript code, I have one inner function with the name Manager with the outer or parent function called Employee. On calling the Employee() function, nothing happened. Let's try to an alert.

alert(s);

It showed me the output as:

output

It's showing code written within Manager since I returned Manager instead of calling the function. But if I try to call only s(), it will alert me with the message:

alert

Another way of calling a method is Employee(‘Pradeep')() instead of s(). This is called Closure.

Employee(‘Pradeep')();

You may know that in JavaScript all local variables and function are properties to a special internal object called the LexicalEnvironment. This LexicalEnvironment in a browser is the window.

The LexicalEnvironment forms a list of properties chaining that executes from inside out, in other words Manager has access to the Manager function itself along with the variable "time" and the function "Employee" with its parameter "name". The outer function variable and parameter would act like a global variable for an inner function.

//LexicalEnvironment = window = {name:...,Employee:function}
    function Employee(name) {
        var time = 5; //LexicalEnvironment = window = {time:5, name:...,Employee:function}
        function Manager() //LexicalEnvironment = window = {Manager:function}
        {
            //LexicalEnvironment = {}
            alert(name + ' will attend the meeting at ' + time);
        }
        return Manager;    }
 

Continuing with the same example, if I try to call s() again, it will display the output as:

Pradeep will attend the meeting at 6

How is it possible that when the variable time is declared within Employee() and incremented within the inner function, it wasn't reset when called again?

  Actually the answer for this is, the variable time with latest value is still being referenced by the inner function Manager(). So every time you call s(), time will be incremented.

This concept where in:

  • A function within a function with an inner function can access the outer function variables and parameters keeping a reference to the outer LexicalEnvironment.
  • The browser keeps the LexicalEnvironment and all its variables in memory until there is an inner function that references it, is called Closure.

Problem

Now let's consider a problem where closure is creating a problem for me. I am trying to create 3 buttons and attaching an onclick event to it, where the clicking of a button causes an alert message to be shown. Seems simple, right?

for (i = 1; i <= 3; i++) {
    btnObj = document.createElement("button");
    btnObj.textContent = i + ' Star';
    btnObj.onclick = function () {
       alert("You have voted " + i + " Star");
    };
    document.body.appendChild(btnObj);
}
 

onclick event 

As expected, the output will be 3 buttons and on the click of each button a respective alert is displayed. But no. You will observe that on the clicking of a button it will display a message as:

display message

This is where Closure is relevant. As said before, closure preserves the latest value until the function "onclick" is referenced even though the loop has completed, the last value is stored in i is 4 .This is the reason it will display weird output.

Solution

Oh, how to solve this issue? The same closure will help me to solve it. Let's see. :)

Solution

for (i = 1; i <= 3; i++) {
   btnObj = document.createElement("button");
   btnObj.textContent = i + ' Star';
   btnObj.onclick = (function (k) {
        return function () {
             alert("You have voted " + k + " Star");
        };


   } (i));
     document.body.appendChild(btnObj);


Notice that this time our onclick function is actually being invoked automatically and the function actually returns another function. This allows us to pass in the value of i into that function (that is using the variable k in the alert) since it was when the function was created. Now we will have the correct value for the button when it is clicked.

Closure in jQuery

var btnlist = $("button");
    btnlist.each(function (index, obj) {
        $(obj).click(function () {
            alert("Clicked " + $(this).text() + " having index-" + (index + 1) + " of " + btnlist.length + " buttons");
        });
    });
 

In the above example, as you can observe the callback function of the click event is able to access the parameter "obj" and "index" along with the btnlist array. This is how Closure is used even in jQuery.

Conclusion

The Closure mainly has three scope chains:

  1. It has access to its own scope (LexicalEnvironment),
  2. It has access to the outer function's variables and parameters
  3. It has access to the global variables. This is by default known to everyone.

Closures are extensively used in jQuery library, Node.js, asynchronous architecture. I hope you like this article.