Mastering JavaScript Closures: Straightforward Examples for Beginners

Mandeep KaurMandeep Kaur
5 min read

Before diving into the concept, let me first show you the code and help you understand why it behaves the way it does.


function parent() {
// 10 acre 
  let property = 10;

  return function child() {
    console.log("I have " + property + " acres of property");
  }
}

parent()(); // Output: I have 10 acres of property

We know that JavaScript is a single-threaded language, which means it has one call stack.

When a function finishes executing — like parent() here — it's removed from the stack, right?

So if parent() is gone, how does the inner child() function still remember the value of property? 🤯

That’s happening because in JavaScript, functions can form closures — meaning they remember the variables from their lexical scope, even after the outer function has finished executing.

In our example, child() was defined inside parent(), so it "closes over" the property variable.
As a result, even though parent() has returned, child() still has access to property — because it was part of the scope where child() was created.

Think of it like this:
👉 Even when the parent() function "dies" (i.e., finishes executing), the property still belongs to the child() function — because the child has a strong reference to it.

That’s exactly what a closure is:

A function that remembers the variables from the scope where it was created — even if that outer function has already finished executing.

In JavaScript, this is made possible because of lexical scoping — where functions “remember” the environment in which they were born.

⭐ Important Clarification:

Closure Keeps Reference, Not Value

A closure doesn't store a snapshot of the variable's value —
it keeps a reference to the original variable in memory.

So if the variable changes later, the closure will see the updated value.

function parent() {
  let property = 10;

  const child = function() {
    console.log(property);
  };

  property = 100;

  return child;
}

const childFn = parent();
childFn(); // 100 ✅ because it refers, not copies

In short: Closure = Function + Lexical Scope bundled together

Let’s Touch on Some Advanced Closure Concepts:

Now that we understand the basics of closures, let’s explore some more advanced and interesting scenarios where closures play a crucial role — including one of the classic “gotchas” that often trips up JavaScript developers: closures inside loops.

Closures Inside Loops — The Classic JavaScript Gotcha

One of the trickiest and most common problems involving closures happens when you use them inside loops.

Imagine you write a loop that schedules some functions to run later — like with setTimeout. You might expect each function to remember the current loop value. But surprisingly, all the functions end up sharing the same final value instead!

Let’s see why this happens, and how understanding closures can help us fix it.

Closures Inside Loops

Consider this code snippet:

for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// What do you think this will print?

You might expect it to print:

1
2
3

But surprisingly, it prints:

4
4
4

Why is that?

What’s Going On?

The reason lies in how closures and the var keyword work together. The var variable i is function-scoped, so there is only one i shared by all the functions inside the loop.

By the time the setTimeout callbacks run (after 1 second), the loop has already finished, and i has the value 4 (which caused the loop to stop).

All the closures reference the same i, so they all print the same final value.

How to Fix the Closure Inside Loop Problem

Solution 1: Use let Instead of var

The easiest and most modern way is to use let for the loop variable, because let is block-scoped. This means every iteration gets its own separate i.

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

Each closure now remembers the value of i for that particular iteration.

Solution 2: Use an Immediately Invoked Function Expression (IIFE)

If you want to support older JavaScript environments without let, you can use an IIFE to create a new scope each iteration:

for (var i = 1; i <= 3; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j);
    }, 1000);
  })(i);
}
// Output: 1 2 3

Here, j is a new parameter passed to the IIFE, capturing the current value of i. Each closure then references its own copy j.

Summary

  • Closures capture variables by reference, not value.

  • Using var causes closures in loops to share the same variable.

  • Use let or an IIFE to create new scope for each iteration and fix the issue.

Why Are Closures So Powerful?

Closures are more than just a neat trick — they are fundamental to how JavaScript manages variables and functions:

  • Preserve State: Closures let functions “remember” variables from their creation context, even after the outer function has finished running.

  • Enable Data Privacy: They help us create private variables that can’t be accessed directly from outside.

  • Support Functional Programming: Closures are essential for patterns like currying, partial application, and higher-order functions — concepts we’ll explore in more detail later in this series.

  • Handle Asynchronous Code: They allow callbacks, promises, and event handlers to access the variables they need.

Understanding closures gives you deeper insight into JavaScript’s behavior and lets you write more elegant, bug-free code.

Quick Summary

  • A closure is a function bundled together with its lexical scope.

  • Closures allow inner functions to remember variables from their outer functions, even after those outer functions finish.

  • Variables inside closures are referenced, not copied, which affects behavior in loops.

  • Using let or IIFEs helps avoid common closure pitfalls in loops.

Final Thoughts

Closures may seem tricky at first, but once you grasp how they work, they become one of your most powerful tools in JavaScript. Remember, closures help functions remember their surrounding state, enabling you to write cleaner, more modular, and more expressive code.

Keep practicing with different examples, and soon you’ll see closures popping up everywhere in your projects!

If you found this post helpful, feel free to share it and follow me for more JavaScript tips and deep dives.

Happy coding! 🚀

0
Subscribe to my newsletter

Read articles from Mandeep Kaur directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mandeep Kaur
Mandeep Kaur

Frontend Engineer 👩🏻‍💻 | Crafting seamless and responsive user interfaces with React & modern JavaScript | Passionate about clean code, UI/UX, and performance optimization | Sharing frontend tips, tricks, and tutorials