Blog / Others/ JavaScript Closures Explained: A Complete Guide with Practical Examples

JavaScript Closures Explained: A Complete Guide with Practical Examples

If you have been coding in JavaScript for any length of time, you have probably heard the word "closure" thrown around. Maybe you have seen it in interview questions, or maybe you have used it without even realizing it. Either way, closures are one of those concepts that can feel mysterious at first—but once you get it, everything clicks.

What Exactly Is a Closure?

A closure is simply a function that has access to variables from its outer (enclosing) scope, even after that outer function has finished executing.

Think about it this way: when you create a function inside another function, that inner function "remembers" the environment in which it was created. That is the closure in action.

Here is a basic example:

function outerFunction() {
  const name = "John";
  
  function innerFunction() {
    console.log("Hello, " + name);
  }
  
  return innerFunction;
}

const myFunction = outerFunction();
myFunction(); // Output: "Hello, John"

Even though outerFunction has finished running by the time we call myFunction, the inner function still has access to the name variable. That is a closure.

Why Does This Matter?

Closures are incredibly powerful because they allow you to:

  • Create private variables - Variables that cannot be accessed from outside
  • Maintain state - Remember values between function calls
  • Create function factories - Generate specialized functions on the fly

Practical Example: Counter

Here is a classic use case—creating a counter with private state:

function createCounter() {
  let count = 0;
  
  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
console.log(counter.count); // undefined - count is private!

The count variable is completely hidden from the outside world. No one can modify it directly—they can only interact with it through the methods we provide.

Practical Example: Function Factory

Closures also shine when you need to create specialized functions:

function createGreeting(greeting) {
  return function(name) {
    return greeting + ", " + name + "!";
  };
}

const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");
const sayHowdy = createGreeting("Howdy");

console.log(sayHello("John"));    // "Hello, John!"
console.log(sayHi("Jane"));       // "Hi, Jane!"
console.log(sayHowdy("Bob"));    // "Howdy, Bob!"

Each returned function "remembers" its specific greeting, even though they all use the same underlying code.

Common Pitfalls

The Loop Problem

One of the most famous closure issues looks like this:

// This does not work as expected!
for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// Output: 4, 4, 4 (not 1, 2, 3!)

The problem? var does not create block scope, so all three functions share the same i. By the time the timeout runs, the loop has finished and i is 4.

The Fix:

// Solution 1: Use let (creates block scope)
for (let i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// Output: 1, 2, 3

// Solution 2: Use an IIFE (Immediately Invoked Function Expression)
for (var i = 1; i <= 3; i++) {
  (function(index) {
    setTimeout(function() {
      console.log(index);
    }, 1000);
  })(i);
}
// Output: 1, 2, 3

When You Will Use Closures

Here is where closures show up in real code:

  • Event handlers - Callbacks that remember the context in which they were attached
  • React hooks - useState and useEffect both rely on closures internally
  • Module patterns - Creating private state in JavaScript (before ES6 classes)
  • Function decorators - Functions that modify other functions
  • Async code - Callbacks that need access to variables from their surrounding scope

The Key Takeaway

At its core, a closure is just a function that remembers the environment where it was created. It is not some abstract concept—it is a practical tool that lets you:

  • Keep data private
  • Maintain state without global variables
  • Write more expressive, flexible code

Once you start recognizing closures in your code, you will see them everywhere. And honestly, you have probably been using them already without calling them by name.

That is the thing about closures—they just work. And now you know why.

Post a Comment

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