What is a Closure? Why Do I Need to Use Closure?

What is a Closure? Why Do I Need to Use Closure?

Issue

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.

Lexical Scope

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.

Closure

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.

Conclusion

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:

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.
Author

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.

Did you find this article helpful?
NoYes

Comments (1)

Leave a comment...
Avatar
Đông Nguyễn Văn1 year ago
Bài viết hay, em cảm ơn tác giả ạ.Nhưng ở đoạn kết quả của nó lại là 4 4 4 4 4.Em nghĩ là 5 5 5 5 5 đúng không ạ?
Reply
Avatar
Xuân Hoài Tống1 year ago
@gif [Q2aN4iiaibCus] Xin lỗi, đúng là mình có sai sót và đã sửa lại bài viết. Cảm ơn bạn nhé.