DEV Community

Cover image for JavaScript Closures in Action: Real-World Applications
Joseph42A
Joseph42A

Posted on

JavaScript Closures in Action: Real-World Applications

If you just started out learning javascript and spends a few month with it, or even if you've used javascript for a while, chances are you probably didn't face an issue that required knowledge of javascript closure, or actually maybe behind the scene you already used closure without realizing it.

Closures are considered an advanced topic in javascript, understanding closures is crucial for writing more complex and efficient JavaScript code, let's start to learn more about it.

Understanding Closures

Closures occur when a function is defined within another function (the outer function) and has access to variables from the outer function's scope.

As we all know functions in javascript have access to variables outside of their scope.

 const c = 10;

 const add = (a, b) => {
     return a + b + c; 
 }

 console.log(add(1,1))
Enter fullscreen mode Exit fullscreen mode

As you can see we accessed variable 'c' even though it is outside of add function.

When javascript first created it didn't support classes, so at that time developers used factory function which is a function that returns an object not a value, here is a simple example of factory function

function createCounter(){
    let value = 0;

    function increment(){
        return ++value
    }

    return {
        increment    
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's create a couple of counters to see what is going on

const counter1 = createCounter();
const counter2 = createCounter();

console.log(counter1.increment()); // 1
console.log(counter1.increment()); // 2
console.log(counter2.increment()); // 1
Enter fullscreen mode Exit fullscreen mode

Now as you see each counter has its own value and keep tracks of it, but how is javascript doing that?! the answer is closure, closure tells us increment() function has access to value variable, but remember each time createCounter() get called we're creating a new value variable so each time incrment() will get access to its own copy of a value variable.

So we can just call closure HIDDEN STATE, and ofcourse its pretty usefull in object oriented programming.

Implementing Memoization Funciton with Closure

One of the core concept of web or programming in general is memoization, it is also a technique that applied in dynamic programming, basically memoization is realy just a fancy word for caching, so we are just caching the result of the function so that we don't excute the function if we know the result.

const add = (a, b) => {
    return a + b;
}

add(1,2); // Excute
add(1,2); // Excute
add(2,2); // Excute
Enter fullscreen mode Exit fullscreen mode

Normally when we call a function javascript will not cache the result for you, it is just calling and excute the function but if we add a memoizaiton it will only excute when the result is changing,

So here is the solution of creating a memoization function that takes a function as its argument and handle the HIDDEN STATE (closure) to track the result of the function's excustion

function memoize (fn){
    let cache = {}
    return function(...args){
        const key = JSON.stringify(args);
        if(key in cache) {
            return cache[key]
        }
        cache[key] = fn(...args);
        return cache[key]
    }
}
Enter fullscreen mode Exit fullscreen mode

We used cache variable as a HIDDEN STATE and stored the function parameters as key and result of the function as the value in a Hash Map, with that we can use our memoize function like this

const add = memoize((a, b) => {
    return a + b;
})

console.log(add(1,2)); // Excute
console.log(add(1,2)); // Doesn't Excute
console.log(add(2,2)); // Excute
Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, closures are a powerful and often misunderstood concept in JavaScript. They allow functions to retain access to variables from their lexical scope, even after the outer function has finished executing. Closures are essential for creating private variables, implementing function factories, and optimizing code through techniques like memoization. Understanding closures is crucial for writing more complex and efficient JavaScript code.

Top comments (5)

Collapse
 
efpage profile image
Eckehard • Edited

Is there any way to protect the external context to "leak" inside a function context? This might happen accidentially changing existing code:

 let c = 10, B=3;

 const add10 = (a, b) => {
     c = a*2 // omitting let affects the global scope
     ( ... )
     return B + c; 
 }

 console.log(add10(1))
Enter fullscreen mode Exit fullscreen mode
Collapse
 
joseph42a profile image
Joseph42A

We can simply declare a locale variable inside the function to prevent this

let c = 10, B=3;

const add10 = (a, b) => {
    let c = a * 2; // Declare c as a local variable
    // Other operations
    return B + c; 
}

console.log(add10(1)); // Output: 5
Enter fullscreen mode Exit fullscreen mode
Collapse
 
efpage profile image
Eckehard

You know Murphy's law:

β€œAnything that can go wrong will go wrong.”

IΒ΄m not talking about intentional behavoir, but about possible and hard to track errors. As the global scope is always "vulnerable" in Javascript, it is a good habit not to use any global variables at all. But this is not so easy if you use HTML-IDΒ΄s:

See this example:

<body>
<h1> headline </h1>
<p /id="a">text</p>  
<script>
     console.log(a) // => <p id="a">...</p>
</script>
</body>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ

Closures occur when a function is defined within another function (the outer function) and has access to variables from the outer function's scope.

This is not correct. A closure is created whenever a function is created - nested functions are irrelevant.

Collapse
 
joseph42a profile image
Joseph42A

Thanks Jon for your comment!
While it's true that closures are created whenever a function is defined, including nested functions, the concept is often explained in the context of nested functions because that's where the effects of closures are most noticeable and useful. The ability of inner functions to access variables from their outer scope, even after the outer function has finished executing, is a key aspect of closures that is often illustrated using nested functions.