1

The following program executes in an unexpected way. Why is this exactly? I have some vague understanding based on the fact that the closure returns the reference to i and not the value of i. And since the value of i at the end is 3, then it will apply those values all across.

function idCreator (peopleList) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < peopleList.length; i++) {
      peopleList[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }
    return peopleList;
}

var myFriends = [{name:"ABC", id:0}, {name:"PQR", id:0}, {name:"XYZ", id:0}];

var createIdForMyFriends = idCreator (myFriends);

var abcID = myFriends [0];
console.log(abcID.id()); // 103
user3490188
  • 499
  • 2
  • 6
  • 14

4 Answers4

3

The problem is that every closure that you create in your loop is referencing the same variable i; the value of i when the function executes is not necessarily the value when the closure was created. (In your case, it will be the value of i when the loop terminates, which is 3.) You can fix this using a separate function that returns the closure:

function idCreator (peopleList) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < peopleList.length; i++) {
      peopleList[i]["id"] = closureCreator(i);
    }
    return peopleList;
}

function closureCreator(i) {
    return function ()  {
        return uniqueID + i;
    };
}

Here's a way using an IIFE:

peopleList[i]["id"] = (function(loop_i) {
    return function()  {
        return uniqueID + loop_i;
    };
)(i));
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
2
for (i = 0; i < peopleList.length; i++) {
  peopleList[i]["id"] = function ()  {
    return uniqueID + i;
  }
}

In this code, the i in each of the functions you are creating is referencing the same i. An i that will be changed before any of these functions are ran. When they finally are ran, they will all use the same (final) value of i.

You need to "capture" the value of i at each iteration and use that.

function idCreator (peopleList) {
    var i;
    var uniqueID = 100;

    var createFunc = function(i){
        return function(){
            return uniqueID + i;
        };
    };

    for (i = 0; i < peopleList.length; i++) {
      peopleList[i].id = createFunc(i);
    }

    return peopleList;
}
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • +1 for not using an IIFE. – cookie monster Apr 17 '14 at 15:02
  • 2
    @cookiemonster What is wrong with using an IIFE? – Alex W Apr 17 '14 at 15:03
  • IIFE's are fine, this is just an alternative way to do it. Personally, I think this just looks cleaner (and is easier to write) than `peopleList[i].id = (function(i){ return function(){ return uniqueID + i }; }(i));`. But, either way is fine. – gen_Eric Apr 17 '14 at 15:04
  • @AlexW: Ugly syntax that adds clutter without enhancing clarity. Notice all the questions on SO from people who don't understand what's happening with that syntax. Not to mention that it creates an extra new function in every iteration of the loop. – cookie monster Apr 17 '14 at 15:05
0

You don't need closure.

function idCreator (peopleList) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < peopleList.length; i++) {
      peopleList[i]["id"] = uniqueID + i;
    }
    return peopleList;
}

var myFriends = [{name:"ABC", id:0}, {name:"PQR", id:0}, {name:"XYZ", id:0}];

var createIdForMyFriends = idCreator (myFriends);

var abcID = myFriends [0];
console.log(abcID.id);

if still want use closure use following code

function idCreator (peopleList) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < peopleList.length; i++) {
     (function(i){
       peopleList[i]["id"] = function(){
          return uniqueID + i;
      }
     })(i);
    }
    return peopleList;
}

var myFriends = [{name:"ABC", id:0}, {name:"PQR", id:0}, {name:"XYZ", id:0}];

var createIdForMyFriends = idCreator (myFriends);

var abcID = myFriends [0];
console.log(abcID.id());

Your closure function always return last value of i as it execute after end of for loop;

Anoop
  • 23,044
  • 10
  • 62
  • 76
-1

The variable i is resolved at the time you call the .id() method, not at the time the method is defined. So after the loop runs, the value of i is always 3, regardless of which object you call .id() on.

Roger Powell
  • 245
  • 1
  • 3