I’m having trouble with JavaScript closures when creating functions inside loops. Here’s what happens when I try to make an array of functions:
var callbacks = [];
// creating 3 callback functions
for (var counter = 0; counter < 3; counter++) {
// storing each function in the array
callbacks[counter] = function() {
// expecting each to print its own counter value
console.log("Counter is:", counter);
};
}
for (var k = 0; k < 3; k++) {
// executing each callback
callbacks[k]();
}
This prints:
Counter is: 3
Counter is: 3
Counter is: 3
But I want it to print:
Counter is: 0
Counter is: 1
Counter is: 2
The same issue happens with event handlers:
var elements = document.querySelectorAll(".my-button");
for (var counter = 0; counter < elements.length; counter++) {
elements[counter].onclick = function() {
console.log("Button number:", counter);
};
}
classic closure trap! u can also use bind for event handlers: elements[counter].onclick = function(index) { console.log("Button number:", index); }.bind(null, counter); - bind locks the counter value. array methods like forEach avoid this issue since they create their own scope.
The issue arises because var is function-scoped, meaning after the loop ends, counter retains its final value of 3, which all closures reference. To address this, switch to let, as it creates a new binding for each iteration. For instance:
Yeah, this tripped me up hard when I started with JavaScript. The problem is all your functions share the same variable reference instead of grabbing the value when they’re created. You can fix this with forEach instead of a regular for loop:
forEach creates a new execution context each time through, so counter gets captured properly. I like this approach when you’re working with existing arrays rather than just counting. Same thing works for your async example - forEach captures each value correctly without needing let or IIFE tricks.