Skip to main content

Command Palette

Search for a command to run...

JavaScript Closure gotchas by reference not value

Updated
•1 min read•View as Markdown

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