Understanding Closures in JavaScript

Bhavesh JadhavBhavesh Jadhav
3 min read

Closures in JavaScript can seem like a complex concept, but they are fundamental to understanding how the language works. In essence, a closure is a function bundled together with its lexical environment. This means that a function, along with the variables it was declared with, forms a closure. This bundled structure allows the function to access those variables even after it has been executed outside its original scope.

Uses of Closures

Closures are incredibly powerful and versatile, and they have several practical applications in JavaScript:

  • Module Design Pattern: Encapsulating private data.

  • Currying: Creating functions with preset arguments.

  • Functions like Once: Ensuring a function is called only once.

  • Memoization: Caching results of expensive function calls.

  • Maintaining State in Async World: Managing state across asynchronous operations.

  • SetTimeouts: Delaying execution of code.

  • Iterators: Generating sequences of values.

Example: setTimeout and Closures

Consider the following example to understand how setTimeout interacts with closures:

function x() {
    var i = 1;
    setTimeout(function() {
        console.log(i);
    }, 3000);
    console.log("Namaste JavaScript");
}
x();

In this example, many might think that JavaScript’s setTimeout will wait before executing the callback function. However, JavaScript does not wait. It prints "Namaste JavaScript" first, then waits for 3000 milliseconds before printing the value of i. The callback function forms a closure, remembering the reference to i, and after the timer expires, it logs the value of i.

Common Pitfall

Let’s examine a common mistake when using setTimeout inside a loop:

function x() {
    for (var i = 1; i <= 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, i * 1000);
    }
    console.log("Namaste JavaScript");
}
x();

You might expect this code to print "Namaste JavaScript" followed by 1, 2, 3, 4, 5, each after a second. However, the output is "Namaste JavaScript" followed by 6 five times. Why does this happen?

Explanation:

Due to closure, all setTimeout callbacks remember the reference to i, not its value. By the time the timers expire, the loop has completed, and i equals 6. All callbacks then log the final value of i.

Fixing the Issue

To fix this issue, use let instead of var:

function x() {
    for (let i = 1; i <= 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, i * 1000);
    }
    console.log("Namaste JavaScript");
}
x();

Here, let creates a new block-scoped variable i for each iteration, resulting in the desired output: "Namaste JavaScript", then 1, 2, 3, 4, 5 each after a second.

Achieving the Same Without let

If you must use var, you can create a new scope using a function:

function x() {
    for (var i = 1; i <= 5; i++) {
        (function(i) {
            setTimeout(function() {
                console.log(i);
            }, i * 1000);
        })(i);
    }
    console.log("Namaste JavaScript");
}
x();

In this version, the immediately invoked function expression (IIFE) creates a new scope, capturing the value of i for each iteration. This ensures each setTimeout callback logs the correct value.

Conclusion

Closures are a powerful feature in JavaScript that allow functions to remember their lexical environment. Understanding closures and how they work with asynchronous code, such as setTimeout, is crucial for mastering JavaScript. By leveraging closures, you can write more robust and maintainable code.


By understanding and using closures effectively, you can tackle complex programming challenges in JavaScript with confidence and ease. Happy coding!

10
Subscribe to my newsletter

Read articles from Bhavesh Jadhav directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bhavesh Jadhav
Bhavesh Jadhav

I am a passionate web developer from India, currently pursuing an MCA from Government College of Engineering, Aurangabad. With a strong foundation in HTML, CSS, JavaScript, and expertise in frameworks like React and Node.js, I create dynamic and responsive web applications. My skill set extends to backend development with PHP, MySQL, and MongoDB. I also have experience with AJAX, jQuery, and I am proficient in Python and Java. As an AI enthusiast, I enjoy leveraging AI tools and LLMs to enhance my projects. Eager to expand my horizons, I am delving into DevOps to optimize development workflows and improve deployment processes. Always keen to learn and adapt, I strive to integrate cutting-edge technologies into my work. Let's connect and explore the world of technology together!