Hello guys, Today we are going to go over the concept of closure in JavaScript.
Background Terms
IIFEs aka Immediately Invoked Function Expressions are JavaScript functions that run as soon as they are defined. I am already writing a detailed article regarding IIFEs. So until then, if you do not know about IIFEs, just think of it as an anonymous function that runs automatically and here is the default syntax for it.
(function () {
//Write your code here
})()
Lexical scope is the concept of a variable defined outside a function being accessible inside another function defined after the variable declaration
What is a Closure?
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time
Let me simplify. Think you have an IIFE that has a variable called total.
(function () {
let total = 0
})();
Usually, since the total variable is defined inside the IIFE as a local variable, we can only use the total variable inside the IIFE.
But there is one way around it. If you define another function inside the IIFE, then that function also can access the variables of the parent function (IIFE) and hence can access the total. Here is how that would look like.
(function () {
let total = 0
function print() {
console.log("Here is the total : ", total)
}
print();
})();
This prints total's current value (0) on the console.
So, now you would say, "Right, but still the total variable is only accessible from inside the IIFE". Yes, you are absolutely right. So, let's just return the function.
You can think of functions in JavaScript as another variable type. We can just return the function and assign it into a variable. And since we are assigning the function reference to a variable, we don't even have to have a name for the inner function. Cool, right?
const addition = (function () {
let total = 0
return function (){
total += 1
console.log("The total is : ", total)
}
})();
addition()
addition()
Here is the console output
The total is : 1
The total is : 2
Please note that the outer function doesn't have to be an IIFE. You can use the same concept with normal functions as well like in the following code snippet. I'm just using IIFEs to make the code cleaner.
//Using Normal Functions
function addition() {
let total = 0;
return function () {
total += 1;
console.log("The total is : ", total);
};
}
//This returns the same console output as before
let returningFunction = addition()
returningFunction()
returningFunction()
You can just call this function as much as you want now. You are updating the value of a local variable from outside the function. This is the concept of closure in JavaScript.
If you want, we can take this a step further by returning a set of functions like this.
const manipulate = (function () {
let total = 0
return {
increment : function () {
total += 1
console.log("The total is : ", total)
},
decrement: function () {
total -= 1
console.log("The total is : ", total)
}
}
})();
manipulate.increment()
manipulate.increment()
manipulate.decrement()
Here is the console output
The total is : 1
The total is : 2
The total is : 1
Multiple Nested Functions
Take a look at this example from the MDN Docs
// global scope
var e = 10;
function sum(a){
return function(b){
return function(c){
// outer functions scope
return function(d){
// local scope
return a + b + c + d + e;
}
}
}
}
console.log(sum(1)(2)(3)(4)); // log 20
As you can see we can just keep writing anonymous functions inside functions and use all the passed values. This also can be done using named functions as well. But you have to write few more lines when calling the functions.
// global scope
var e = 10;
function sum(a){
return function sum2(b){
return function sum3(c){
// outer functions scope
return function sum4(d){
// local scope
return a + b + c + d + e;
}
}
}
}
var sum2 = sum(1);
var sum3 = sum2(2);
var sum4 = sum3(3);
var result = sum4(4);
console.log(result) //log 20
Conclusion
Well, that is pretty much all you need to know to get started with JavaScript closures. Try to think of where you can add closure in your codebases. We will only get better if we start practising these concepts on a daily basis.
If you want to learn more in-depth stuff regarding closures like performance considerations, you should check out the MDN Docs article.
Thank you for reading my article. I hope you learned something valuable today. If you did, don't forget to follow my blog and share the article with your friends. And more importantly, stay safe 😷