Skip to main content

Command Palette

Search for a command to run...

JavaScript Closure gotchas by reference not value

Updated
1 min read

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 :)