DEV Community

Cover image for Demystifying JavaScript Closures: A Deep Dive with Examples
Nilupul Perera
Nilupul Perera

Posted on

2 1 1 1 1

Demystifying JavaScript Closures: A Deep Dive with Examples

Closures are one of the most fundamental yet confusing concepts in JavaScript. They form the backbone of many powerful patterns, from private variables to function factories. Understanding closures is key to writing efficient, modular, and secure JavaScript code.

In this post, we’ll explore:

βœ… What closures are

βœ… How they work with examples

βœ… Where to use them in real-world applications


πŸš€ What Are Closures?

A closure is a function that "remembers" the variables from its outer scope even after the outer function has finished executing.

This means that even when a function is executed outside its original scope, it still has access to the variables from that scope.

πŸ”Ή Basic Example

function outerFunction() {
  let message = "Hello from closure!";

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

  return innerFunction;
}

const myClosure = outerFunction();
myClosure(); // Output: Hello from closure!
Enter fullscreen mode Exit fullscreen mode

Even though outerFunction has finished executing, innerFunction still remembers and has access to the message variable. This is closure in action!


πŸ›  How Closures Work

JavaScript uses lexical scoping, which means functions can access variables from their outer (parent) scope.

Let’s break it down with an example:

function createCounter() {
  let count = 0;

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

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

Even though createCounter is done executing, the returned function still remembers the count variable!

πŸ“Œ Closure Scope Chain

Closures follow a scope chain, which determines how variables are resolved:

  1. If a variable is found in the current function’s scope, it is used.
  2. If not, JavaScript looks up to the parent scope.
  3. This continues until JavaScript reaches the global scope.

πŸ“Œ Real-World Use Cases for Closures

Closures have many practical applications in JavaScript. Let’s explore some:

1️⃣ Data Encapsulation (Private Variables)

Closures allow us to create private variables that can’t be accessed directly.

function secretPassword() {
  let password = "superSecure123";

  return {
    checkPassword: function (input) {
      return input === password;
    }
  };
}

const passwordManager = secretPassword();
console.log(passwordManager.checkPassword("wrong")); // false
console.log(passwordManager.checkPassword("superSecure123")); // true
Enter fullscreen mode Exit fullscreen mode

The password variable is hidden inside the closure and can only be accessed through checkPassword.


2️⃣ Function Factories

Closures let us create dynamic functions with saved values.

function multiplier(factor) {
  return function (num) {
    return num * factor;
  };
}

const double = multiplier(2);
console.log(double(5)); // 10

const triple = multiplier(3);
console.log(triple(5)); // 15
Enter fullscreen mode Exit fullscreen mode

Each returned function "remembers" its factor, making it reusable!


3️⃣ Event Handlers & Asynchronous Code

Closures are often used in event listeners and callbacks to retain state.

function createClickHandler(message) {
  return function () {
    console.log(message);
  };
}

document.getElementById("myButton").addEventListener("click", createClickHandler("Button clicked!"));
Enter fullscreen mode Exit fullscreen mode

Each click handler retains its own message variable.


⏳ Common Pitfalls with Closures

Closures are powerful but can introduce memory leaks if not handled properly.

⚠️ Issue: Memory Leaks

function leakyFunction() {
  let bigData = new Array(1000000).fill("πŸš€");

  return function () {
    console.log(bigData.length);
  };
}

const leak = leakyFunction();
Enter fullscreen mode Exit fullscreen mode

Since bigData is stored in the closure, it remains in memory even after execution.

βœ… Solution: Nullify References

Manually remove unused references:

let leak = leakyFunction();
leak = null; // Garbage collector removes bigData from memory
Enter fullscreen mode Exit fullscreen mode

🎯 Conclusion

Closures are a fundamental concept in JavaScript that allow functions to "remember" their surrounding scope. They enable:

βœ… Encapsulation (private variables)

βœ… Function factories (dynamic functions)

βœ… Efficient event handling

By mastering closures, you can write cleaner, more modular, and optimized JavaScript code. πŸš€

Do you use closures in your projects? Drop a comment below! πŸ‘‡

AWS Q Developer image

Your AI Code Assistant

Implement features, document your code, or refactor your projects.
Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)