All Blogs

Learn & Apply: JavaScript Closures

Learn about the JavaScript Closures and why they are important

JavaScript is one of the most popular programming languages in the world, widely used for both client-side and server-side applications. One of its most powerful features, often not fully understood by newcomers, is the concept of closures. Closures are a fundamental aspect of JavaScript, enabling powerful programming techniques and helping manage scope in ingenious ways. In this blog, we'll explore what closures are, how they work, and why they are so useful, providing examples to illustrate these concepts clearly.

Learning

What is a Closure?

At its core, a closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created. Essentially, closures allow a function to access those variables from an outer function even after the outer function has finished execution, which leads to various useful applications.

Lexical Scoping Explained

To understand closures, you first need to understand lexical scoping. JavaScript uses lexical scoping, meaning that a function's ability to access variables is determined by its location within the source code. Let's consider a simple example to see how lexical scoping works:

let scope = "global scope"; // A global variable

function checkscope() {
  let scope = "local scope"; // A local variable
  function f() {
    return scope; // Accesses the local variable
  }
  return f();
}

checkscope(); // Returns "local scope"

In this example, checkscope() defines a local variable scope and a function f() that returns the value of the local variable. The function f() is executed within the same scope it was defined, which is why it returns "local scope."

How Closures Work

Now, let's modify the previous example slightly:

let scope = "global scope";
function checkscope() {
  let scope = "local scope";
  function f() {
    return scope;
  }
  return f;
}

let s = checkscope()(); // What does this return?

In this version, checkscope() returns the function f() itself, rather than the result of f(). When f() is called (s()), it still accesses the local scope variable from checkscope(). Despite being executed outside of its original scope, f() remembers the scope variable. This capability to "remember" the environment in which it was created is the essence of a closure.

Practical Uses of Closures

Closures have numerous practical uses, such as:

  • Data Privacy: Closures can create private variables and methods. This is useful in many design patterns, like the module pattern, where we encapsulate some of the code to shield it from the global namespace and create private states.

  • Event Handlers: They can maintain state in an event without needing global variables or complex object properties.

  • Functional Programming: Closures facilitate functional programming patterns, such as currying and partial applications.

Advanced Example: Creating Private States

Consider this advanced example where closures are used to create private states:

function counter() {
  let n = 0;
  return {
    count: function () {
      return n++;
    },
    reset: function () {
      n = 0;
    },
  };
}

let c = counter(),
  d = counter();
console.log(c.count()); // => 0
console.log(d.count()); // => 0: they count independently
c.reset();
console.log(c.count()); // => 0: because we reset c
console.log(d.count()); // => 1: d was not reset

In this case, each call to counter() creates a new closure with its own n. The count and reset methods are both closures sharing access to the same n.

Conclusion

Closures are a powerful feature of JavaScript, providing both utility and efficiency in code management. They allow for elegant ways to handle data privacy, manage state, and write more maintainable and error-free code by keeping everything local to its execution context. Understanding closures is crucial for any JavaScript developer looking to harness the full potential of this versatile language.

By learning the concepts of closures and lexical scoping, developers can write cleaner, more efficient, and more secure JavaScript code, paving the way for advanced programming techniques and applications. Whether you're managing private states or handling complex application flows, closures are an important part of the JavaScript toolkit.