DEV Community

pythonassignmenthelp.com
pythonassignmenthelp.com

Posted on

How I Finally Understood JavaScript Closures While Building a Simple Web App

You're staring at your assignment, and the error makes no sense. You just want a button click to update a counter, but JavaScript keeps messing up the numbers. You add more console.logs, you tweak things, but every time you run it, something's off—maybe all your buttons update the same value, or nothing updates at all. If you've ever found yourself shouting at your screen about variables not behaving, you're not alone. I was completely lost on closures until I built a simple web app and finally saw how everything fits together.

What Even Is a Closure? (And Why Should You Care?)

When I first heard "closure," I thought it was just another fancy JavaScript term professors love throwing around. But closures are everywhere in JS, especially when you start building interactive apps—counters, to-do lists, games, you name it.

A closure happens when a function "remembers" the variables from its outer scope, even after that outer function has finished running. That's it. The magic is in how functions hold on to variables, letting you build flexible, reusable code.

But honestly, the definition never helped me until I saw it in action. So let's build something together.


Example 1: The Broken Counter Buttons

Suppose your assignment is to generate 3 buttons. Each should count its own clicks. You think: "Easy! I'll just use a loop."

<!-- index.html -->
<div id="buttons"></div>
<script>
  // We'll create 3 buttons in a loop
  const container = document.getElementById('buttons');
  for (var i = 1; i <= 3; i++) {
    const btn = document.createElement('button');
    btn.textContent = `Button ${i}: 0`;
    let count = 0;
    btn.addEventListener('click', function() {
      count++;
      btn.textContent = `Button ${i}: ${count}`;
    });
    container.appendChild(btn);
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Try this in your browser. What you'll see is that each button looks correct at first, but the label doesn't update as you expect. Sometimes the number is wrong, or all buttons act weirdly.

What's Going On?

  • The i variable is shared among all iterations of the loop because of var.
  • The callback function in addEventListener is using the same i value after the loop finishes. All buttons end up saying "Button 4" (since i is 4 after the loop).
  • The count variable is fine here because it's a new variable per button, but the label is tied to the wrong i.

This is where closures—and how you capture variables—really matter.


Example 2: Fixing the Counter with Closures

Here's how you can fix it, using a closure to "trap" the i value for each button.

<!-- index.html -->
<div id="buttons"></div>
<script>
  const container = document.getElementById('buttons');
  for (var i = 1; i <= 3; i++) {
    // We use an IIFE (Immediately Invoked Function Expression)
    (function(currentI) {
      const btn = document.createElement('button');
      btn.textContent = `Button ${currentI}: 0`;
      let count = 0;
      btn.addEventListener('click', function() {
        count++;
        btn.textContent = `Button ${currentI}: ${count}`;
      });
      container.appendChild(btn);
    })(i); // Pass the current value of i to the IIFE
  }
</script>
Enter fullscreen mode Exit fullscreen mode

How does this work?

  • The IIFE (that weird (function(currentI) { ... })(i); bit) creates a new scope each time through the loop.
  • Each button's click handler "remembers" its own currentI—this is the closure in action.
  • Now, each button counts up and shows the right number.

When I first saw this, it was like, "Wait, so the function still knows what currentI was when it was created—even after the loop is done?" Yup. That's the closure: the callback holds on to the variables it needs.


Digging Deeper: Closures Without the DOM

You don't need the DOM or event listeners to see closures. Here's a simple function to generate counters.

function createCounter(start) {
  let count = start;
  // The returned function "remembers" the count variable
  return function() {
    count++;
    return count;
  };
}

const counterA = createCounter(0);
const counterB = createCounter(10);

console.log(counterA()); // 1
console.log(counterA()); // 2
console.log(counterB()); // 11
console.log(counterB()); // 12
Enter fullscreen mode Exit fullscreen mode

Why does this work?

  • createCounter returns a new function each time.
  • That returned function closes over the count variable—it keeps its own copy, even after createCounter is done running.
  • Each counter is independent. counterA and counterB don't interfere with each other.

This pattern is how a lot of libraries and frameworks manage state.


How Closures Show Up in "Real" Web Apps

When I was tutoring someone on a React project, they ran into the same confusion: "Why does my event handler see the wrong value?" The thing is, JavaScript functions don't forget the variables they need—they hang onto them. That's why closures are at the heart of things like hooks, callbacks, and async code.

If you're working on a JavaScript or TypeScript project and need more examples or step-by-step walkthroughs, this resource has helped students work through these concepts with practical code.


Common Mistakes Students Make

1. Using var Instead of let or const

With var, variables are function-scoped, not block-scoped. That means all your callbacks might accidentally use the same variable. Prefer let or const in loops:

for (let i = 0; i < 3; i++) {
  // Each iteration gets its own 'i'
}
Enter fullscreen mode Exit fullscreen mode

2. Expecting Functions to "Forget" Variables

Some students think that once a function returns, its variables disappear. But if an inner function uses them, they're kept alive as long as that inner function exists.

3. Not Realizing the Function Is Created Inside the Loop

If you declare your handler outside a loop or factory function, all buttons or items will share the same state. Always create your function inside the place where the unique variables exist.


Key Takeaways

  • A closure is when a function remembers variables from its outer scope—even after that outer function is done.
  • Closures are everywhere in JavaScript: event handlers, setTimeout callbacks, and even React hooks.
  • You can use closures to create independent state for each function—like a counter or a unique label.
  • Be careful with var in loops—it can cause bugs because all your functions share the same variable.
  • If you want each callback to have its own copy of a variable, use let, const, or an IIFE to create a new scope.

Understanding closures honestly took me a while, but once it clicked, writing JavaScript got a lot less mysterious. If you keep running into weird variable bugs, remember: closures aren't out to get you—they're just how JS keeps track of what your functions need. Stick with it, experiment, and soon it'll feel natural.


Want more JavaScript/TypeScript tutorials and project walkthroughs? Check out https://pythonassignmenthelp.com/programming-help/javascript.

Top comments (0)