Closures are one of the fundamental concepts of JavaScript that every JavaScript developer should know and understand. Yet, it's a concept that confuses many new JavaScript developers. Having an accurate understanding of closures will help you to write better, more efficient, and clean code.
In this article, we will try to explain the internals of closures and how they work in JavaScript with the help of the following topics:
-
What are Closures in JavaScript?
-
When to use closure function?
- First, using the closure function for implementing encapsulation in JavaScript.
- Second, using the closure function for implementing iterators in JavaScript.
- Third, using closure function for implementing singleton in JavaScript.
What are Closures in JavaScript?
A closure is a function that has access to its outer function scope even after the return of the outer function. It means a closure can access variables and arguments of its outer function even after the function has finished.
Before understanding the concept of closure functions, let's understand the accessibility of variable in various scopes of a program with the help of the following image:
As we can see from the above image, the inner function has access to variables of the outer function and the global scope. Now, whenever we invoke a function, a new scope is created for that call. The local variable declared within the function belongs to that scope. When the function has finished the execution, the scope usually destroys. So, how to ensure that variables in the outer function scope are still accessible by the inner scoped function even after the outer scoped function has finished execution. The closure functions can also achieve the same. Let's understand the same with the examples in the below sections:
- Let's comprehend the concept of closure function with the help of the following example. Suppose you want to implement a counter which returns the next value of a variable whenever invoked:
<html>
<body>
Demonstrating execution scope in javascript: </br>
<script>
var index = 0;
function counter() {
index += 1; // increment its current value.
document.write("Value of counter is: " + index + "</br>");
}
// Invoke the function
counter();
counter();
counter();
</script>
</body>
</html>
- Save the file with name executionScope1.html and open it in any browser (Chrome, Firefox, or IE). It should show the output as:
- In the above example, we were able to implement the counter functionality, but it has a flaw; the index variable is global, any script on the page would have access to it and could change it without calling the counter function. We need to make sure that the counter function only modifies the index variable. Therefore, move the definition of the variable inside the function, so that no one can change it without calling the function counter as shown below:
<html>
<body>
Demonstrating execution scope in javascript: </br>
<script>
function counter() {
var index = 0;
index += 1; // increment its current value.
document.write("Value of counter is: " + index + "</br>");
}
// Invoke the function
counter();
counter();
counter();
</script>
</body>
</html>
-
Save the file with name executionScope2.html and open it in any browser (Chrome, Firefox, or IE). It should show the output as:
-
As is evident from the above code snippet that now the index variable is not accessible from the outside (as the function scope does not protect it), but this doesn't generate the needed output as every time you call the function, it will reassign the index variable to 0. So every time the function invokes, it just prints the value 1.
This can be solved by creating an internal function, as shown below:
<html>
<body>
Demonstrating closure in javascript: </br>
<script>
function initializeCounter() {
var index = 0;
var counter = function() {
index += 1;
document.write("Value of counter is: " + index + "</br>");
};
return counter;
}
// Invoke the function
var counter = initializeCounter();
counter();
counter();
counter();
</script>
</body>
</html>
-
Save the file with name closure.html and open it in any browser (Chrome, Firefox, or IE). It should show the output as:
-
As is evident from the above code snippet and screenshot, we declared an anonymous internal function, which has access to the index variable in the outer scope, and this variable is accessible to this method even after initializecounter() method has returned. This internal method is called a closure function.
So, we can say that a closure is a function that has access to the variable from the outer function's scope, and one can accomplish this by creating a function inside a function. Also, the closure function serves as the gateway between the global context and the outer scope. So, we can summarize closure functions as:
- Closure functions are nested function which has access to the outer scope.
- After the outer function is returned, by keeping a reference to the inner functions, called as closures, we can prevent the outer scope from destroying.
Now, let's try to understand in which scenarios, we should use the closure functions.
When to use Closure functions in JavaScript?
As we have seen in the above example that closure can access the variables in the outer scope and can persist the data across function calls. The same functionality makes the usage of closure functions suitable for the following use cases:
- First, using closure for implementing encapsulation in JavaScript.
- Second, using closure for implementing iterators in JavaScript.
- Third, using closure for implementing singleton in JavaScript.
Let's understand all these use-cases in detail:
Using closure for implementing encapsulation in JavaScript
Even though JavaScript doesn't enforce the information hiding concept, but sometimes to follow the OOPs concepts, a programmer can structure their programs to assume variables as private variables and provide getter and setter methods to access and set values for these variables. It's achievable with the help of closure functions very easily.
- Consider the following code snippet to understand this in detail:
<html>
<body>
Demonstrating closure for Encapsulation in javascript: </br>
<script>
function initializeData() {
var myVar = 1;
return {
getVar: function() {
return myVar;
},
setVar: function(value) {
myVar = value;
}
};
}
obj = initializeData();
document.write("Initial value of variable: " + obj.getVar() + "</br>");
obj.setVar(2);
document.write("Variable value after update to int: " + obj.getVar() + "</br>");
obj.setVar("Test Value");
document.write("Variable value after update to string: " + obj.getVar() + "</br>");
</script>
</body>
</html>
-
Save the file with name closureAsGetSet.html and open it in any browser (Chrome, Firefox, or IE). It should show the output as:
-
As we can see from the above example, we declared two closure functions getVar and setVar, which can get and set the value of the outer variable "myVar" and we can use them as getters and setters for the variable.
Using closure for implementing iterators in JavaScript
As we know that the data from the outer scope is preserved, creating iterators with closures can be very easy.
- Let's try to understand the details with the help of following code snippet:
<html>
<body>
Demonstrating closure as iterators in javascript: </br>
<script>
function buildCounter(index) {
var counter = index;
var displayConunter = function() {
document.write("Value of counter variable is: " + counter + "</br>");
counter++;
};
return displayConunter;
}
var myCounter = buildCounter(1);
myCounter();
myCounter();
myCounter();
// Create a new counter
var myNextCounter = buildCounter(10);
myNextCounter();
myNextCounter();
myNextCounter();
// Validate that the previous counter is not impacted.
myCounter();
myCounter();
</script>
</body>
</html>
-
Save the file with name closureAsIterator.html and open it in any browser (Chrome, Firefox, or IE). It should show the output as:
-
As we can see from the above example that the displayCounter() function serves as closure and gives the next value of the variable counter, whenever we invoke the closure. Also, we can validate that the two functions myCounter() and myNextCounter() maintain their reference to the outer variables and keep their counters. The objects don't intermingle or overwrite the values.
Using closure for implementing singleton in JavaScript
A singleton is an object that has just one instance during the execution of a program. It is easy to achieve that in javascript with the help of closures. We acknowledge the fact that every function call creates a new closure. But how do we prevent another call of the outer function? It's achievable if we don't give a name to the closure function.
- Let's understand this in details, with the help of following code snippet:
<html>
<body>
Demonstrating closure as singleton in javascript: </br>
<script>
var singleton = function () {
var counter = 0;
return {
get: function () {
return "Counter: " + counter;
},
increment: function() {
counter++;
}
};
}(); // Attention Here - the singleton is the result of this function's call
document.write("Value of counter: " + singleton.get() + "</br>");
document.write("Value of counter: " + singleton.get() + "</br>");
singleton.increment();
document.write("Value of counter after increment: " + singleton.get() + "</br>");
singleton.increment();
document.write("Value of counter after another increment: " + singleton.get() + "</br>");
</script>
</body>
</html>
- Save the file with name closureAsIterator.html and open it in any browser (Chrome, Firefox, or IE). It should show the output as:
We can see that in the above example, the outer function is anonymous. So, we declare it and call it right away, and after that, the only way to access it is by using the singleton object, which is the closure.
Key Takeaways
- A closure is a function that has access to its outer function's variables and methods even after the outer function has completed its execution.
- Moreover, we use Closures mainly for the implementation of encapsulation, iterators, and singleton in JavaScript.
Let's move to the next article to understand one for the concept of JavaScript for handling the asynchronous nature using "Async/Await in JavaScript".