JavaScript Closure gotchas by reference not value
Closure sometimes behave undesirably, especially when you want to get by value and not by reference.
Consider
function outer() {
let arr=[{accumulated:-1},{accumulated:-2},{accumulated:-3}];
let i;
for (i=0; i<3; i++) {
arr[i]["accumulated"]=function() {
return i; // all 3
}
}
return arr;
}
const result=outer();
console.log(result[0].accumulated()); // all 3
console.log(result[1].accumulated());
console.log(result[2].accumulated());
Output is all 3 because .accumulated() method's content is function() {return i;} and here i is not replaced dynamically but assigned after for loop has ended. So i=3 is applied to all.
To fix this you could do
function outer() {
let arr=[{accumulated:-1},{accumulated:-2},{accumulated:-3}];
let i;
for (i=0; i<3; i++) {
arr[i]["accumulated"]=function(j) {
return j;
}(i) // execute this anonymous function immediately.
}
return arr;
}
const result=outer();
console.log(result[0].accumulated); //0
console.log(result[1].accumulated); //1
console.log(result[2].accumulated); //2
But the most obvious fix is
function outer() {
let arr=[{accumulated:-1},{accumulated:-2},{accumulated:-3}];
//let i;
for (let i=0; i<3; i++) {
arr[i]["accumulated"]=function() {
return i;
}
}
return arr;
}
const result=outer();
console.log(result[0].accumulated()); //0
console.log(result[1].accumulated()); //1
console.log(result[2].accumulated()); //2
Just declare let i inside for loop. That's it :)