Skip to content

Instantly share code, notes, and snippets.

@kukiron
Created November 4, 2017 11:30
Show Gist options
  • Save kukiron/b4c13f5e6d5bc852f5a3281041debe5f to your computer and use it in GitHub Desktop.
Save kukiron/b4c13f5e6d5bc852f5a3281041debe5f to your computer and use it in GitHub Desktop.

This describes the common mistake with using closures in JavaScript.

A function defines a new environment

Consider:

function makeCounter()
{
  var obj = {counter: 0};
  return {
    inc: function(){obj.counter ++;},
    get: function(){return obj.counter;}
  };
}

counter1 = makeCounter();
counter2 = makeCounter();

counter1.inc();

alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0

For each time makeCounter is invoked, {counter: 0} results in a new object being created. Also, a new copy of obj is created as well to reference the new object. Thus, counter1 and counter2 are independent of each other.

Closures in loops

Using a closure in a loop is tricky.

Consider:

var counters = [];

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = {
      inc: function(){obj.counter++;},
      get: function(){return obj.counter;}
    }; 
  }
}

makeCounters(2);

counters[0].inc();

alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1

Notice that counters[0] and counters[1] are not independent. In fact, they operate on the same obj!

This is because there is only one copy of obj shared across all iterations of the loop, perhaps for performance reasons. Even though {counter: 0} creates a new object in each iteration, the same copy of obj will just get updated with a reference to the newest object.

Solution is to use another helper function:

function makeHelper(obj)
{
  return {
    inc: function(){obj.counter++;},
    get: function(){return obj.counter;}
  }; 
}

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = makeHelper(obj);
  }
}

This works because local variables in the function scope directly, as well as function argument variables, are allocated new copies upon entry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment