Introduction
In this article, we will learn about closures in JavaScript.
Every JavaScript developer should be aware of closures as it's the most frequently asked interview question as well as understanding it clearly will help you to avoid creating bugs in your code.
So let's get started.
What is closure?
Before diving into closures, we first need to understand how scope works in JavaScript.
Take a look at the below code:
function display() {
var i = 10;
console.log(i); // 10
}
display();
console.log(i); // Uncaught ReferenceError: i is not defined
When we declare a variable with var/let/const
keyword inside a function, it's only available inside that function which means it's scoped to that function only because it becomes a private variable.
If you execute the above code, you will see that inside the function, console.log
will print the value 10 as the variable i
is available inside the function scope.
But outside the function the variable will not exist so when we try to print it, Uncaught ReferenceError
is displayed.
Now, take a look at the below code:
function add(x) {
var sum = 5;
function result(y) {
sum = sum + (x * y);
return sum;
}
return result;
}
var output = add(4); // output will contain the result function
console.log(output(5)); // 25
console.log(output(10)); // 65
In the above code, we have defined result
function inside the add
function and from the add
function we're returning the result
function.
So in the first console.log
we get 25
as the output because sum
is initialized to 5 inside the function so 5 + (4 * 5) = 25.
When we again call the function stored in the output
variable by passing 10, even though the sum
function has finished executing, it will still retain the value of sum
variable calculated last time which was 25 so the output will be 65 because 25 + (4 * 10) = 65.
So here comes the definition of a closure.
A function when defined inside another function and is returned from the outer function, will retain access to all the variables that were in the scope when the inner function was defined even after the outer function is terminated.
So in short in the above code, when we defined the result
function, it has access to the sum
variable and we're returning the result
function from the add
function so the result
function will retain access to the sum
variable even after the add
function has finished executing.
This is known as closure.
Closures are very useful as it provides a way to create private variables which are only accessible from inside the function and will retain its value across the function calls.
So we cannot access the sum
variable directly and it's only accessible through the call to the result
function avoiding the misuse of the sum
variable.
Take a look at the below code:
function getCharacter(name) {
let i = 0;
return function next() {
const value = name[i++];
return value;
}
}
const next = getCharacter('hello');
console.log(next()); // h
console.log(next()); // e
console.log(next()); // l
console.log(next()); // l
console.log(next()); // o
In the above code, variable i
will retain its value across the multiple function calls so we're able to get each character in every function call using closures.
We can re-write the above code as below:
function getCharacter(name) {
let i = 0;
return {
next: function() {
const value = name[i++];
return value;
}
};
}
const obj = getCharacter('hello');
console.log(obj.next()); // h
console.log(obj.next()); // e
console.log(obj.next()); // l
console.log(obj.next()); // l
console.log(obj.next()); // o
In the above code, we're returning an object from the function and the object has function as a property so it will also be a closure.
Now, take a look at the below code:
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
This is a very famous interview question.
You might think that the above code will print numbers from 0 to 4 but it actually prints the number 5 five times.
This is because we have used setTimeout
function inside the for loop so by the time, the setTimeout
executes(which is after 1 seconds), the for loop is already finished executing.
So the value of i
is 5 when the for loop is finished so the setTimeout
function will print the value 5 five times.
There are two ways we can fix this issue.
- using closures:
for(var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000)
})(i);
}
The above code will correctly print the values from 0 to 4.
This is because by wrapping the setTimeout
call in a function, we're creating a unique scope for each interaction.
So the value we're passing to the inner function at (i)
will be passed to the function as a parameter function(i)
.
- using let:
ES6 has added a let
keyword which creates a new scope for each iteration when used inside a loop so we can just use the let
keyword instead of var
which will correctly print the value from 0 to 4.
for(let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Now, take a look at the below code:
var numbers = {};
for(var i = 0; i < 10; i++){
numbers[i] = function() {
console.log(i);
}
}
console.log(numbers[0]()); // 10
console.log(numbers[1]()); // 10
console.log(numbers[2]()); // 10
Here, also we get the value 10
printed every time instead of the actual value of the variable i
.
We can fix this in 2 ways.
- using
let
instead ofvar
- defining the function outside the loop like this:
var numbers = {};
function log(value) {
return function getValue() {
return value;
}
}
for(var i = 0; i < 10; i++){
numbers[i] = log(i);
}
console.log(numbers[0]()); // 0
console.log(numbers[1]()); // 1
console.log(numbers[2]()); // 2
In the above code, we have defined the getValue
function inside the log
function so we created a closure that will retain the value of i
which was passed to it.
So we get the correct expected output.
Conclusion
That's it about this article.
I hope you're now clear with using closures in JavaScript.
Don't forget to subscribe to get my weekly newsletter with amazing tips, tricks, and articles directly in your inbox here.