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.
What is Closure in JavaScript?
In JavaScript, closure refers to the ability of a function to remember and access its lexical scope (the set of variables and functions available to it at the time of its creation) even when the function is executed outside that scope.
This means that a function can access and manipulate variables that were defined in its outer function, even after the outer function has finished executing. The inner function that is returned from the outer function, along with its captured variables, forms a closure.
Closures are often used in JavaScript to create private variables and functions and implement callbacks and event handlers. As a result, they are a powerful feature of the language that can help make code more modular, flexible, and maintainable.
How to use closure in JavaScript?
Let's understand the use of closure with a couple of examples.
I was supposed to create three buttons dynamically and attach the onclick event to them, alerting 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>
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. So let's try to alert.
alert(s);
It showed me the output as follows:
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:
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 functions are properties of 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 as 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?
The answer to this is that the inner function Manager is still referencing the variable time with the latest value (). So every time you call s(), time will be incremented.
This concept wherein:
- 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 an inner function references it, called Closure.
Problem
Now let's consider a problem where closure creates a problem for me. I am trying to create three buttons and attaching an onclick event to them, where clicking a button causes an alert message to be shown. It 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);
}
As expected, the output will be three buttons, and a respective alert will be displayed at the click of each button. But no. You will observe that on the clicking of a button, it will display a 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. :)
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 invoked automatically, and the function returns another function. This allows us to pass the value of i into that function (that is, using the variable k in the alert) since it was when the function was created. So 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 click event's callback function can access the parameters "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:
- It has access to its own scope (LexicalEnvironment),
- It has access to the outer function's variables and parameters
- It has access to global variables. This is, by default, known to everyone.
Closures are extensively used in the jQuery library, Node.js, and asynchronous architecture. I hope you like this article.