DEV Community

Cover image for Understanding JavaScript Closures ⚡️
Ali Samir
Ali Samir

Posted on

Understanding JavaScript Closures ⚡️

JavaScript closures are a fundamental concept that every developer should understand.

They can seem complex at first, but once you grasp the concept, you'll realize how powerful they are for writing efficient and clean code.

This article aims to demystify closures by explaining what they are, how they work, and providing practical examples of their use.


📌 What is a Closure?

A closure is a feature in JavaScript where an inner function has access to its outer enclosing function’s variables. The closure has three scopes:

1- Local Scope: Variables defined within its function.
2- Outer Function’s Scope: Variables defined in the outer function.
3- Global Scope: Variables defined in the global scope.

Closures allow a function to access variables from an enclosing scope even after that scope has finished executing.

This means that a function retains access to its lexical scope, which is the environment in which it was created.


⭐️ How Do Closures Work?

To understand closures, let’s look at a simple example:

function outerFunction() {
  let outerVariable = 'I am from the outer function';

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

  return innerFunction;
}

const myClosure = outerFunction();
myClosure(); // Outputs: I am from the outer function
Enter fullscreen mode Exit fullscreen mode

In this example, outerFunction defines a variable outerVariable and an inner function innerFunction.

The innerFunction logs outerVariable to the console.

When outerFunction is called, it returns the innerFunction, which is then assigned to myClosure.

Even though outerFunction has finished executing, myClosure still has access to outerVariable because of the closure.


⚡️ Practical Uses of Closures

Closures are useful in many scenarios, including:

1. Data Privacy

Closures can be used to create private variables.

By defining variables within a function scope and returning an inner function that accesses these variables, you can create private variables that cannot be accessed directly from the outside.

function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Enter fullscreen mode Exit fullscreen mode

In this example, count is a private variable that can only be accessed and modified through the returned function.


2. Function Factories

Closures can be used to create function factories—functions that return other functions. This is useful for creating functions with predefined behavior.

function createAdder(x) {
  return function(y) {
    return x + y;
  };
}

const addFive = createAdder(5);
console.log(addFive(10)); // 15
console.log(addFive(20)); // 25
Enter fullscreen mode Exit fullscreen mode

Here, createAdder returns a function that adds a given number to x, demonstrating how closures can capture and use arguments from their outer scope.


3. Maintaining State in Asynchronous Code

Closures are also useful in asynchronous code, such as callbacks and event handlers, where maintaining state is crucial.

for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log('i:', i);
  }, 1000 * i);
}
// Outputs: i: 4, i: 4, i: 4 (incorrect due to var's function scope)
Enter fullscreen mode Exit fullscreen mode

To correct this, we can use a closure to capture the current value of i:

for (let i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log('i:', i);
  }, 1000 * i);
}
// Outputs: i: 1, i: 2, i: 3 (correct due to let's block scope)
Enter fullscreen mode Exit fullscreen mode

Alternatively, with var, we can achieve the same result using an immediately invoked function expression (IIFE):

for (var i = 1; i <= 3; i++) {
  (function(i) {
    setTimeout(function() {
      console.log('i:', i);
    }, 1000 * i);
  })(i);
}
// Outputs: i: 1, i: 2, i: 3
Enter fullscreen mode Exit fullscreen mode


Conclusion ✅

JavaScript closures are a powerful feature that allows functions to access variables from their enclosing scope even after that scope has finished executing.

They are essential for creating private variables, function factories, and maintaining state in asynchronous code.

By understanding and leveraging closures, you can write more efficient and maintainable JavaScript code.

Whether you're new to JavaScript or looking to deepen your understanding, mastering closures will enhance your ability to solve complex problems and write cleaner code.


Happy Coding! 🔥

Top comments (0)