Closure is an important concept in programming that allows you to implement functions more easily.
Closure is also quite popular in the world of JavaScript programming. Some people may have never heard of closure but may have inadvertently used it, while others may have heard of it but not fully understood it due to its abstract nature. So let's continue reading this article to explore more.
First of all, let me introduce a little bit about Lexical scope: in a group of nested functions, the inner functions have access to variables and other resources within the scope of their parent function. Lexical scope is sometimes also called Static scope.
For example:
function foo() {
const a = 1;
function bar() {
console.log(a);
}
bar(); // 1
}
Even though the variable a
is not inside the bar
function, bar
is inside foo
so it can still access the variable a
.
Similarly, closure follows the principle of Lexical scope. It can access variables of other functions outside of its own scope, as well as global variables. However, one important thing to note is that closures can still retain the state of the variables inside them. In other words, every time you return a function or assign a function to a variable, it carries the values of all the dependent variables.
For example:
function add(x) {
return function addTo(y) {
return x + y;
}
}
const addFive = add(5);
const addToTen = addFive(10);
console.log(addToTen); // 15
In the above example, the add
function takes a parameter x
and then returns a function that takes a parameter y
and returns the sum of x
and y
.
First, when we call add(5)
, we might think that the variables x
and y
in add
will no longer exist. However, after calling addFive(10)
, we still get the result as 15. This means that the state of the add
function is still preserved even after the function has been executed. If it wasn't preserved, addFive(10)
wouldn't know that the value of the previous call to x
was 5.
From this, we understand that when add
returns the addTo
function, addTo
is enclosed in a context that includes both x
and y
at that particular moment.
So, now that we understand the theory behind closures, what are their benefits?
First, let's revisit a classic example:
for(var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
We expect the result to be 0 1 2 3 4
, but unfortunately, the actual result is 5 5 5 5 5
. This is because setTimeout
is only executed after the loop has finished iterating, and at that point, the reference value of the variable i
in the console.log
functions is already 5.
To solve this problem, we can replace var
with let
, or we can use closures to encapsulate the setTimeout
and create a separate context for the function at that moment:
for(var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => {
console.log(j);
}, 0);
})(i);
}
In addition, closures are also used to create scopes for object properties.
Consider the following example:
class Person {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
setName(name) {
this.name = name;
}
}
The getName
and setName
functions are added to prevent direct access to name
. However, because JavaScript classes do not support access modifiers, name
can still be easily modified as usual.
const p = new Person();
p.setName('estacks');
p.getName(); // estacks
p.name = 'edited';
p.name; // edited;
To prevent this, let's try using a closure:
function Person() {
let _name;
const getName = () => {
return _name;
}
const setName = (name) => {
_name = name;
}
return {
getName,
setName,
}
}
const p = Person();
p.setName('estacks');
p.getName(); // estacks
p._name; // undefined
The Person
function returns two closure functions that have access to _name
.
As you can see, direct access to the _name
variable is not possible. Any operations involving _name
must be done through the setName
and getName
functions.
Another application of closures is with curry functions. For those who have not read or are not familiar with curry functions, you can read my article What is a Curry Function? A Delicious "Curry" Dish and How to Enjoy It?. Closures make it easier than ever to create a curry function, and it has a lot of practical applications as well.
Closure is not just a concept specific to JavaScript; many other languages also support it. Closure is a function that follows the principles of Lexical scope and has the ability to retain the state of the variables it depends on. Closure has many important applications, such as creating access modifiers, curry functions, and more. Closure is also an important knowledge in interviews to assess your understanding of the JavaScript language.
References:
Hello, my name is Hoai - a developer who tells stories through writing ✍️ and creating products 🚀. With many years of programming experience, I have contributed to various products that bring value to users at my workplace as well as to myself. My hobbies include reading, writing, and researching... I created this blog with the mission of delivering quality articles to the readers of 2coffee.dev.Follow me through these channels LinkedIn, Facebook, Instagram, Telegram.
Comments (1)