DEV Community

Cover image for Understanding Closures in JavaScript with Practical Examples
Manu Kumar Pal
Manu Kumar Pal

Posted on

Understanding Closures in JavaScript with Practical Examples

Hey DEV community! 👋 Today, we’re unpacking one of the most fundamental features in JavaScript:

What is a Closure?

A closure in JavaScript occurs when an inner function remembers and accesses variables from its outer function’s scope, even after the outer function has finished executing. This happens because functions “close over” their surrounding variables.

Closures allow you to:

-> Keep data private

-> Create powerful factory functions

-> Write flexible, modular code

-> Remember state in callbacks and event handlers

Simple Example of a Closure

function outer() {
  const message = "Hello from the outer scope!";

  function inner() {
    console.log(message); // inner can access outer's message
  }

  return inner;
}

const myClosure = outer(); // outer() runs and returns inner()
myClosure(); // logs: "Hello from the outer scope!"
Enter fullscreen mode Exit fullscreen mode

What’s happening here?

-> outer() runs and returns the inner() function.
-> Even after outer() finishes, inner() still remembers the message variable from outer()’s scope.

Practical Example: Creating Private Variables
Closures are a great way to encapsulate data and avoid polluting the global scope.

function createCounter() {
  let count = 0; // private variable

  return {
    increment() {
      count++;
      console.log(`Count is now: ${count}`);
    },
    decrement() {
      count--;
      console.log(`Count is now: ${count}`);
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();

counter.increment(); // Count is now: 1
counter.increment(); // Count is now: 2
counter.decrement(); // Count is now: 1

console.log(counter.getCount()); // 1

// 🚫 Direct access to 'count' is impossible — it's private!
Enter fullscreen mode Exit fullscreen mode

What’s going on here?

-> count lives inside createCounter()’s scope.
-> The returned object’s methods form closures over count.
-> You control count only through the exposed methods — great for encapsulation!

Common Uses of Closures

✅ Write callbacks (e.g., event handlers, timers)
✅ Implement data privacy in factories or modules
✅ Create partially applied functions
✅ Build memoization utilities to cache results

Pitfall: Closures in Loops

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // logs 3,3,3
}

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // logs 0,1,2
}
Enter fullscreen mode Exit fullscreen mode

Using let creates a new binding each loop iteration, avoiding closure issues with var.

📝 Key Takeaways

-> Closures allow functions to remember outer variables after execution.
-> They enable data encapsulation and modular code.
-> Understanding closures helps prevent bugs and improves maintainability.

🎉 Closures unlocked! Practice more and watch your JS skills soar. Keep coding! 🚀

Top comments (3)

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

this is extremely impressive, especially the bit with private variables. took me ages to understand closures back in the day. you think real privacy with closures is enough for most apps or does it ever fall short

Collapse
 
manukumar07 profile image
Manu Kumar Pal

Thank you for your feedback.

Collapse
 
dotallio profile image
Dotallio

Love how you broke down the var vs let loop issue - that tripped me up so many times early on. Anyone else have wild bugs from this?