A function has access to the scope of the parent function it is defined, even after the execution of the parent function is complete. Basically, in Javascript, the inner function has access to the outer function’s variables and arguments even after the outer function’s execution context is finished. This feature of Javascript is called closure and it is an important concept in Javascript to understand.
The concept of closure can be confusing to grasp if you are new to Javascript. The purpose of this article is to simplify the concept using simple examples so that it can be easy to understand. Let’s get started!
To demonstrate the closure function, we will first create a nested function. We will call the parent function outer()
and the child function inner()
.
function outer() {
let msg = "Hi from outer";
function inner() {
console.log(msg);
}
inner();
}
outer(); // Hi from outer
In the above example, the inner()
function as access to the msg
variable defined in outer()
function because it is part of the same scope.
Now, we want to know if the msg
variable is available even after the execution of outer()
function is complete? To do so we will return the inner()
function as follows.
function outer() {
let msg = "Hi from outer";
return function inner() {
console.log(msg);
}
}
let message = outer(); // execution of outer function is complete
message(); // Hi from outer
// variable 'msg' is still available in inner() function!
As you can see from the above example, inner()
function was able to remember the msg
variable even though the outer()
function completed its execution. This ability to retain the information is what we will call closure in Javascript and it is an in-built property.
This still works if the inner function is an anonymous function.
function outer() {
let msg = "Hi from outer";
return function() {
console.log(msg);
}
}
let message = outer();
message(); // Hi from outer
Let’s look at more examples that will help us understand closure even better.
function sum(argument) {
let number = 1;
return function() {
console.log(number + argument);
}
}
let result = sum(10);
result(); // 11
In the above example, we have a parent function sum
that takes in an argument and returns an anonymous function. Notice, that the inner function not only retains the information about the number
variable but it also stores the information of the argument argument
passed to the parent function which is 10
in this case. When result()
is called the function adds number
and argument
and displays the result 11
.
In the next example, we will create a counter()
function that will increment the count by 1
every time the function is called.
function counter() {
let count = 0;
count++;
console.log(count);
}
counter(); // 1
counter(); // 1
counter(); // 1
counter(); // 1
As you can notice, there is a problem with this function. Every time we call the counter()
function it should increment the count by 1
but the result is always 1
. This is because every time we call counter()
it initializes the count
variable to 0
.
One way to fix this is to use a global variable.
let count = 0;
function counter() {
count++;
console.log(count);
}
counter(); // 1
counter(); // 2
counter(); // 3
counter(); // 4
But this is not really the best way to fix the problem since we want to avoid using global variables. This is where closure comes in handy. Let’s rewrite the code using closure to get the expected output.
function counter() {
let count = 0;
return function() {
count++;
console.log(count);
}
}
let result = counter();
result(); // 1
result(); // 2
result(); // 3
result(); // 4
The first time result()
is called the count
contains value 0
. The next time result()
is called the count
value is already updated as 1
. Every time the result()
function is called the function will retain the value of the count
variable. This makes closure a powerful feature and it also helps us avoid making use of global variables.