Practical usecases of Closures in JavaScript

A closure references variables in the outer scope from its inner scope This allows a function to use those variables within its scope, preserving the context of the outer scope within its inner scope.

· 5 min read
Mary Maina

Mary Maina

I am a frontend engineer passionate about building intuitive user interfaces. Tools: React, Next.JS, TypeScript, React Native, NodeJs

First, let's use an analogy to understand what closures are

Analogy

Imagine you have a treasure box with a special lock that needs a secret key to open it. It is possible to make a copy of this key using special superpowers. Your superpowers keep a copy of this key even after you have closed the lock absence of a key. This means you can use the superpowers to create a secret key to open the box.

The treasure box is like a function and the lock and key are like variables inside a function. The superpowers are like a closure. A closure allows a function to remember the variables even after the function has finished executing, if the function is invoked again it can still access and use those variables.

What are closures?

Let's delve into how closures are used in JavaScript

Closures are like special powers that functions have. Imagine you have a function that creates another function. This inner function can still access and use variables from the outer function, even after the outer function has finished executing. This is possible because of closures.

function outerScope() {
  const name = "John Doe"; // name is a local variable created by outerScoper
  function innerScope() {
    // innerScope() is the inner function, that forms the closure
    console.log(`Hello ${name}`); // use variable declared in the parent function
  }
  innerScope();
}
outerScope();

Let's break the above example further:

  1. Outer Scope Function: This function, serves as the parent scope. Within this function, a local variable called name is declared, along with another function called innerScope. This function is defined within the outerScope function, making it an inner function.
  2. Inner Scope Function: The innerScope function is defined within the outerScope function. Despite being defined within, the innerScope function is only accessible within the outerScope function's lexical scope. It cannot be accessed from outside outerScope.
  3. Closure: The innerScope function has access to the variables declared in its parent scope, which is the outerScope function. The innerScope function does not have its local variables, it can access and manipulate the name variable declared in the outerScope function. This behavior is possible due to closures in JavaScript.
  4. Lifetime: Even after the outerScope function has finished executing, the innerScope function still retains access to the name variable and can continue using it.

Application of closures

  • Functional programming

Closures play an integral role in the implementation of higher-order functions. Higher-order functions take other functions as arguments or return functions as the output. By preserving the behavior of functions within closures, HOF can be created to implement patterns such as map, filter, and reduce enabling developers to write readable code.

// Function to create a multiplier function
function createMultiplier(factor) {
    // This inner function (closure) retains access to the 'factor' variable
    return function(x) {
        return x * factor;
    };
}

// Create multiplier functions for different factors
const double = createMultiplier(2);
const triple = createMultiplier(3);

// Use the multiplier functions
console.log(double(5)); // Output: 10
console.log(triple(5)); // Output: 15
  • Data Privacy

Closures enable the creation of private variables and functions within a function’s scope. Developers can create modules and libraries with hidden implementation details by encapsulating data within closures. This prevents external code from accessing or modifying internal state directly and promotes information hiding thus enhancing data protection.

function createCounter() {
    let count = 0; // This variable is private to the createCounter function

    return {
        increment: function() {
            count++;
        },
        decrement: function() {
            count--;
        },
        getCount: function() {
            return count;
        }
    };
}

const counter = createCounter();

console.log(counter.getCount()); // Output: 0
counter.increment();
counter.increment();
console.log(counter.getCount()); // Output: 2
counter.decrement();
console.log(counter.getCount()); // Output: 1

In this example, the createCounter function returns an object with three methods: decrement, increment, and getCount. These methods are closures that access to the count variable declared in the outer createCounter function. However, the count variable is not accessible from outside the createCounter scope, making it private to the returned object.

  • Event handlers

Closures work with event handlers in JavaScript by allowing inner functions defined within event handlers to retain access to variables from their outer lexical scope. This enables event handlers to maintain context and access relevant data after the outer function has finished running.

  1. Defining Event Handlers: By defining an event handler such as a function to handle form submit, you are creating a callback function that will be executed when an event occurs
  2. Accessing Variables from Outer Scope: Inside the event handler function, you can access variables defined in the parent function due to closures. The inner function retains access to the variables i.e. it is closed over even after the outer function has been executed.
function handleCounter() {
    let count = 0;
    const button = document.getElementById('myButton');
    
    button.addEventListener('click', function() {
        count++;
        console.log('Button clicked ' + count + ' times');
    });
}
handleCounter();

In this example, the event handler function (defined inside addEventListener) has access to the count variable from its outer lexical scope (handleCounter function). Each time the button is clicked the count variable is incremented and logged to the console even after the handleCounter function has finished executing.


share

Mary Maina

I am a frontend devloper