Reputation: 385
I'm sure I'm doing something dumb here, but I'm not sure what. I'm adding anonymous functions to an array for later execution, and creating them in a for loop with variables that change each iteration.
I'm expecting the logged values to be: https:server.net/a https:server.net/b
but instead I'm getting: https:server.net/b https:server.net/b
It looks like maybe when I redefine the function it overwrites the last version of it, but I'm not sure why. I would think that each anonymous function would be different.
Why? What am I doing wrong here?
Here's some sample code:
f = [];
items = ['a', 'b'];
for(var i = 0; i < items.length; i++){
var itemID = items[i];
var itemAccessUrl = `https://server.net/${itemID}`;
var func = function(){
console.log(itemAccessUrl);
};
f.push(func);
}
console.log(f.length);
for(var j = 0; j < f.length; j++){
func();
}
Upvotes: 1
Views: 96
Reputation: 566
What's happening here? When you invoke your array's functions, they use the current value of itemAccessUrl
, i.e. the last assigned string, with char 'b'. This because their invocation happens after the completion of first for-loop.
You can use a closure:
f = [];
items = ['a', 'b'];
for(var i = 0; i < items.length; i++){
var itemID = items[i];
var itemAccessUrl = `https://server.net/${itemID}`;
var func = (function(param) {
return function () {
console.log(param);
};
})(itemAccessUrl);
f.push(func);
}
console.log(f.length);
for(var j = 0; j < f.length; j++){
f[j]();
}
or bind your function to current param:
f = [];
items = ['a', 'b'];
for(var i = 0; i < items.length; i++){
var itemID = items[i];
var itemAccessUrl = `https://server.net/${itemID}`;
var func = (function (param) {
console.log(param);
}).bind(null, itemAccessUrl);
f.push(func);
}
console.log(f.length);
for(var j = 0; j < f.length; j++){
f[j]();
}
Furthermore you have to change the second loop, invoking f[j]();
Upvotes: 1
Reputation: 18281
This is because of the nature of var
scoping. var
is not block scoped, it is "hoisted", as if you declared it at the top of your function.
You could declare itemAccessUrl using let
instead of var, which is block scoped, but it depends on what browser support you require. (Having said that, you're using templated strings, so you should be fine)
Upvotes: 1