Unlocking the Power of Closures: A Journey into JavaScript’s Hidden Gems

·

6 min read

Unlocking the Power of Closures: A Journey into JavaScript’s Hidden Gems

Closures are by far one of the most exciting features of JS, they’re used in countless design patterns yet I see a lot of Js devs struggling with it. Hence, let us delve deeper into this topic to understand its intricacies comprehensively.

Alright, let’s first take a look at what our friendly trusty source of everything Js(MDN)says about closures.

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

Bruh, It's already confusing, right?
So whenever we are talking about Closures we are referencing to a situation involving two functions, one being an outer function and another an inner function.

function outer(){
  function inner(){
    }
}

Now you might think that when do we have such kind of scenario? Let’s take a look at a non-practical yet simple example and then we’ll go into a more detailed one :3

for those of you new js devs, here we have an outer function called me with a variable and an inner function inside it, and right away we called it inside the outer function.
so now if we call me function,

So, this is a non-practical and stupid example, we don't need to do this to console that, but what if we had more functions, more than one? Let’s say...

So, something interesting just happened, something very important, which is the two functions are now sharing the name variable, cause they both have access to the outer scope, that's what makes closures special. Imagine if you had dozens of functions in a real-life application where they need access to dozens of things they need access to, Good stuff! closure saves us from repetitions and even better, what if the name was dynamic??

So, you can see it still works and we use it as many times as we want, the functions have dynamic nature so they’ll react according to the parameters we pass in.
You might wonder, why do we even need the outer function, we can create a global const directly to both of the functions and get rid of the outer one.
Yes, we can do that, But

const name =tyagi; 
       function talk(){
        console.log(`Hi! my name is ${name}`)
    }
        function feel(){
        console.log(`${name} is feeling goooddd.. as we should!`)
    }
        talk();
        feel();

Well, this still works but we can’t use them again anymore.
That’s about why we need the outer function but, it's still not a practical use like, why would anyone need to call a function right away?
let’s say, I want to call the talk function right away but the feel function might be called on a button click,

function me(name){
    function talk(){
        console.log(`Hi! my name is ${name}`)
    }
    function feel(){
    console.log(`${name} is feeling goooddd.. as we should!`)
    }
    talk()
}

One way to do that is by creating an object and have these functions to be methods of that object, and then we can call those methods whenever we want.

So, now this me function becomes a factory function that creates an object and sends it back to you so that you can assign them to variables and stuff.
Another, interesting stuff that just happened is that if we look back to the original me function that we had created, the functions feel or talk had access to the name variable, when it was looking for the name it could just look it up, but now that the me function returns an object that means that the inner function may not be called until later, by the time we call it the me function might have already executed and finished.
Which means that the inner functions are somehow holding onto the value of this variable name, cause when we say dude.talk() we are not executing me anymore and this talk function is holding onto the name value even though it does not exist anymore. That is the magic here, looking up the value or holding on to it for future use.

And, that is the power of closures, they hold on to the reference or whatever else they need which might be gone and can no longer be accessed.

Now let’s take a look at the practical example which is given by our friendly trusty website MDN

Let’s say we had three buttons on our website, and each button would change the font size of the content.

document.getElementById("size-12").onclick=function(){
    document.body.style.fontSize='12px'
} 
document.getElementById("size-14").onclick=function(){
    document.body.style.fontSize='14px'
}
document.getElementById("size-16").onclick=function(){
    document.body.style.fontSize='16px'
}

and the amount of reputation in this small piece of code is painful to watch. So, let’s say I wanted to make this better how can we do that?
for most of us, our instinct would be to look for repetitions which is mostly the logic itself, setting the font size and creating a function for that.

function clickHandler(size){
    document.body.style.fontSize=`${size}px`
}

So now I can use this function on my click handlers and pass the parameters as we want. Let’s try it out

document.getElementById("size-12").onclick=clickHandler(12px)
document.getElementById("size-14").onclick=clickHandler(14px)
document.getElementById("size-16").onclick=clickHandler(16px)

But, can you see why this won't work?
Cause actually we aren’t assigning the function onto onclick, we are assigning the return value cause we are invoking it, and what JS does is that when we call the function it goes to it and does what it needs to do and returns and assigns onclick to that thing which is, in this case, undefined because when assigning a function we shouldn't be invoking it and if I don't invoke it how am I gonna pass the value? and if I want to invoke it and pass the value it needs to return something other than undefined, and if you guessed a function then you’re correct!
So, let’s rewrite our clickhandler

function clickHandler(size){
    retrun function(){document.body.style.fontSize=`${size}px`
      }
}

and now each of the buttons will get a copy of this inner function with size passed in as a dynamic value

document.getElementById("size-12").onclick=clickHandler(12px)
document.getElementById("size-14").onclick=clickHandler(14px)
document.getElementById("size-16").onclick=clickHandler(16px)

and that value right here, for instance, if we say 12 will be kept inside that inner function as a reference inside the function that will return it, and that’s why closures are valuable.
One of the important and practical places you can use Closures is inside HOF Higher order functions. Even though this is one line if it was a real-life world application Closures can save you from a lot of repetitions by writing it one time.I hope this was a great start for you to understand Closures and its use.