What are Closures in Javascript?

closures in js

Beginners often misunderstand closures, but they are still one of the most important concepts in JavaScript.

In simple terms, a closure is a function that has access to variables in its outer scope, even after the outer function has returned.

This means that the inner function “closes over” the variables in the outer function and can access them even when the outer function has completed its execution.

The function creates closures when it defines an inner function inside another function and returns or passes the inner function as an argument to another function.

After the outer function completes execution, the inner function can access the variables in the outer function’s scope.

How Closures Work in JavaScript

To understand closures in JavaScript, we first need to understand the concept of lexical scoping. Lexical scoping is a programming language feature that allows a function to access variables in its parent function’s scope.

By defining a function, one creates a new scope that enables the definition of variables accessible solely to that function and its inner functions.

When JavaScript invokes a function, it creates a new execution context for that function, which involves creating a new scope chain. scope chain is a list of all the variables that the function has access to, starting with its own variables and then looking up the chain to its parent function’s variables.

When defining an inner function inside an outer function, the inner function creates a closure over the variables in the outer function’s scope.This means that the inner function can access those variables even after the outer function has returned, because the variables are still in memory.

Creating Closures in JavaScript

There are several ways to create closures in JavaScript. The most common ways are through function declarations and function expressions.

Lexical Scoping

function outerFunction() {
  const message = "Hello, world!";

  function innerFunction() {
    console.log(message);
  }

  return innerFunction;
}

const myFunction = outerFunction();
myFunction(); // "Hello, world!"

Function Declarations

function createCounter() {
  let count = 0;

  return function increment()
 count++;
    console.log(count);
  }
}

const counter = createCounter();
counter(); // 1
counter(); // 2

Function Expressions

const message = "Hello, world!";

const myFunction = function() {
  console.log(message);
};

myFunction(); // "Hello, world!

Practical Use Cases for Closures

One can use closures in many ways to write better code, making them a potent feature of JavaScript. Here are some practical use cases for closures.

Encapsulating Data

One can use closures to encapsulate data and prevent its access or modification by other parts of the code.

function createPerson(name, age) {
  let _name = name;
  let _age = age;

  return {
    getName: function() {
      return _name;
    },

    getAge: function() {
      return _age;
    },

    setName: function(name) {
      _name = name;
    },

    setAge: function(age) {
      _age = age;
    }
  };
}

const person = createPerson("John", 30);
console.log(person.getName()); // "John"
person.setName("Jane");
console.log(person.getName()); // "Jane"

Event Handlers

Creating event handlers that maintain their state across multiple invocations is another useful application of closures.

function createButton() {
  let count = 0;

  const button = document.createElement("button");
  button.textContent = "Click me!";
  button.addEventListener("click", function() {
    count++;
    console.log(`Clicked ${count} times`);
  });

  return button;
}

const myButton = createButton();
document.body.appendChild(myButton);

Memoization

Memoization, a technique for optimizing expensive function calls by caching their results, is another valuable use of closures.

function memoize(fn) {
  const cache = {};

  return function(...args) {
    const key = JSON.stringify(args);

    if (cache[key]) {
      return cache[key];
    } else {
      const result = fn(...args);
      cache[key] = result;
      return result;
    }
  };
}

function fibonacci(n) {
  if (n === 0 || n === 1) {
    return n;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

const memoizedFibonacci = memoize(fibonacci);

console.log(memoizedFibonacci(10)); // 55
console.log(memoizedFibonacci(11)); // 89
console.log(memoizedFibonacci(10)); // 55 (cached)

memoize is a higher-order function that takes a function fn and returns a new function that caches the results of fn using a closure. The cached results are stored in an object called cache, where the keys are the arguments passed to fn and the values are the results of fn.

fibonacci is a recursive function that calculates the n-th Fibonacci number. memoizedFibonacci is a memoized version of fibonacci that uses the memoize function to cache the results of the function. The first time memoizedFibonacci is called with an argument, it calculates the result using fibonacci and caches the result in the cache object. The next time memoizedFibonacci is called with the same argument, it returns the cached result instead of recalculating it.

closures are a powerful feature of JavaScript that allow functions to access and modify variables outside of their own scope. Understanding closures is essential for writing clear, maintainable JavaScript code.

Follow Us on
https://www.linkedin.com/company/scribblers-den/

Read More
https://scribblersden.com/how-to-integrate-razorpay-in-nodejs/

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *