Get closer to Closures🚢in JavaScript ❀️- Part 2

Get closer to Closures🚢in JavaScript ❀️- Part 2

This article Part 2 will dive into "How Closures work in JavaScript" with practical use cases.

Β·

7 min read

If you haven't checked out my previous article, you can visit this link Part 1 where Closures is thoroughly introduced with code snippets of the execution context, along with some fancy terms made easy, like lexical scope and lexical environment.

Part 2 will touch on the gist of practical use cases around the concept of Closures.

Why do we use closures πŸ€·β€β™€οΈ?

We now understand the definition of Closures given in the MDN better than before that Closure gives you access to an outer function's scope from an inner function. Do you raise your eyebrows 🀨 if I tell you the answer to this question "why we have closures" lies in its definition only?

Yes, you heard it right, it is about accessing the data. Why would we want to access from an outer function instead of globally? Because we have the outer function like a protective shell that wouldn't allow any interference from the global scope.

I understand that at this point, you also would like to see it through some practical examples. No worries, we will do it.

Practical Use Case of Closure πŸ€”

It will be a very simple example, let's dive into it.

πŸ˜‚πŸ€£

Counter Button πŸ”³

Consider a very basic example of a button that initially holds the number 0 and every time the user clicks on it, 0 gets incremented and we see the number of clicks.

Normal approach πŸ˜‹

// At first, we grab the button html element  
const counterBtn = document.getElementsByTagName("button")[0]

// We create the variable counter with 0 value assigned to it initially.
let counter = 0

// We add the "click" event listener that fires when a user clicks a button.
// We call the function incrementCounter when the button gets clicked. 
counterBtn.addEventListener("click", incrementCounter)

// The counter values increases by 1 and gets updated to the inner text of the button.
function incrementCounter () {
    counter++
    counterBtn.innerText = counter
}

// Let's see how it works.

The shortfall of the Normal approach 🀯

  • It was simple and is working fine but there is a problem with the counter variable.

  • When we have many features to build in our code and our file is comprised of many lines of code and by any chance or mistake, we have this later counter = 10. In such a case, if we click on the button, it will start from 0 but increment from 10 instead of 0. Try the below snippet.

      const counterBtn = document.getElementsByTagName("button")[0]
      let counter = 0 
      counterBtn.addEventListener("click", incrementCounter)
      function incrementCounter () {
          counter++
          counterBtn.innerText = counter
      }
      //Assume 500 lines of code here and by mistake
      // and the variable counter is assigned value 10
      counter = 10
    

Changing the Normal approach using Closure 😍

  • We have to solve the problem by making the counter variable private so that any other line or variable which is declared with the same name later doesn't manipulate our counter button's value.

  • Some languages such as Java allow us to declare methods and variables in a class as private, restricting anything outside the class itself to access them.

  • However, JavaScript doesn't allow us to specify whether variables and methods of an object should be private or public in their declaration, but it does give us a way to implement them through Closures and we will do it. Sounds interesting?

      const counterBtn = document.getElementsByTagName("button")[0]
    
      function incrementCounter () {
          let counter = 0
          function increment () {
              counter++
              counterBtn.innerText = counter
          }
          return increment
      }
    
      const retainCount = incrementCounter()
      counter = 10
      counterBtn.addEventListener("click", retainCount)
    
  • We have created an incrementCounter() an outer function with a counter variable set to 0 and the increment() function increments the count variable by 1 and gets updated to the innerText of the counterBtn.

  • Then, the variable retainCount present in our global scope is assigned the result of the incrementCounter() function that is an increment() function only sitting inside it.

  • In JavaScript, when the function is executed, the local variables of the function get destroyed unless there is at least one reference to them. So, after every call, in our code, the outer function's local variable counter gets destroyed.

  • However, the increment() function remembers its lexical environment and has access to the variable of its outer function because it has formed a closure with its outer function incrementCounter().

  • Thus, the counter is increased by 1 and its value becomes 1. In the subsequent function calls on every click, the counter gets increased by 2 and 3, respectively.

  • And even after many lines of code, if there is a counter variable with a value of 10, there is no change in our counter button value because with the help of closure, now the counter variable is treated like a private property and wrapped inside an incrementCounter() function.

  • Give it a try.

Let's make this use case a bit more brainy and practical.

Till now, we had only this counter button and if you notice, it becomes very inconvenient to reload the page every time we go to reset the counter button back to 0. Agree? So, why don't we add a Restart button which will reset the counter main button? Let's do it.

Adding Restart counter button βœ…

  • The first thing that should drop into our mind is, we have two features now instead of one; first, a counter button incrementing the count and second, a restart button bringing the count to 0 on the counter button. This means we will have two functions.

  • Second thing, we will have the count variable with an initial value of 0 only in the outer function and both two inner functions will lexically sit inside the outer function.

  • Let's write the approach in the code now.

      function buttonCounter () {
          let counter = 0
          function incrementCounter () {
              counter++
              counterBtn.innerText = counter
          }
          function restartCounter() {
              counter = 0
              counterBtn.innerText = counter
          }
    

Closure through object 😁

  • It could be done but now how do we return these two functions in a single line altogether? Of course, we do have a way to do this by returning an object which will wrap both functions, just like below.

      function buttonCounter () {
          let counter = 0
          function incrementCounter () {
              counter++
              counterBtn.innerText = counter
          }
          function restartCounter() {
              counter = 0
              counterBtn.innerText = counter
          }
      // It returns an object.
          return {
              incrementCounter: incrementCounter,
              restartCounter: restartCounter,
          };
      }
    
      const retainCount = buttonCounter()
    
      counterBtn.addEventListener("click", retainCount.incrementCounter)
      zeroBtn.addEventListener("click", retainCount.restartCounter)
    
  • This time, the variable retainCount will be assigned a result as an object of both functions.

  • That's why on every click event, the function is called by a dot notation syntax from an object. I hope, it has started making sense now.

  • Now, let's test the code to see if we could solve this simple problem.

Example in brief πŸ“

  • In a nutshell, if we summarize this practical example of Closure, we have achieved the idea of Data encapsulation, one of the advantages of closures, not exposing the variables globally and storing them in a capsule form and giving privacy to the program.

  • Apart from this, there are many other advantages too like creating API calling wrapper methods and implementation of callbacks in javascript and rendering components on state or prop change in React dependent on how closures work.

  • If you would like to add a decrement button in this existing code, you can do it, it would just take a moment to do.

Conclusion 🌟

Closure ❀️ in JavaScript is an interesting and important topic. I hope now you also agree and find yourself comfortable around it when we tried some practical stuff and got closer to Closure 😁.

πŸ˜‚πŸ€£


I would be happy to have your feedback or hear from you on this topic if you explore and bring something new to this piece of article in the comments section. Until then Happy coding and Love JavaScript 😍. Stay tuned, and see you in the thrilling πŸ˜‰Part 3 of getting closer to Closure with setTimeOut.

Β