0

I have a piece of code that I'm trying to have alert 1,2,3. I'm having issues using closures properly, so I can't figure this out.

The original code:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result.push( function() {alert(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

testList();

I am trying to do something like this to buildList() to get it to work properly:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result[i] = function(x) {
            result.push( function() {alert(item + ' ' + list[x])} );   
        }(i);
    }
    return result;
}

I know I'm making mistakes on working with the closures, I'm just not sure what the problem is.

123
  • 8,733
  • 14
  • 57
  • 99

2 Answers2

0

Your second try was closer to the solution but still doesn't work because your inner-most function is capturing variable item from your top-level function: item is just always referencing the same instance, which was created when calling buildList().

var scope in JavaScript is always bound to current function call, not to code block, so it's not bound to control statements like for.

For that reason, the alerts likely show the value 'item' + (list.length-1) had at the time of calling buildList().

Since you are passing i to your closure, you should declare var item within that function, e.g:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        result[i] = function(x) {
            // x and item are both local variables of anonymous function declared just above
            var item = 'item' + list[x]; // or maybe you meant 'item' + x?
            return function() {alert(item + ' ' + list[x])};   
        }(i);
    }
    return result;

}

Note that the closure would still capture a reference to list so will display the value it contains at the time of calling functions in the array returned by buildList(). Also local variable item is completely optional, you could call alert('item' + x /*or is it list[x]?*/ + ' ' + list[x]).

Shadocko
  • 1,186
  • 9
  • 27
0

From How do JavaScript closures work?

Note that when you run the example, "item2 undefined" is alerted three times! This is because just like previous examples, there is only one closure for the local variables for buildList. When the anonymous functions are called on the line fnlistj; they all use the same single closure, and they use the current value for i and item within that one closure (where i has a value of 3 because the loop had completed, and item has a value of 'item2'). Note we are indexing from 0 hence item has a value of item2. And the i++ will increment i to the value 3.

You need to make a closure in each loop iteration if you are to store the matching value of i:

function buildList(list) {
  var result = [], item, closure;
  for (var i = 0; i < list.length; i++) {
    item = 'item' + list[i];

    // call this function with the string you wish to store
    // the inner function will keep a reference to the 'msg' parameter even after the parent function returns
    closure = (function(msg) {
        return function() {
          alert(msg);
        };
      }(item + ' ' + list[i]));
    result.push( closure );
  }
  return result;
}

function testList() {
  var fnlist = buildList([1, 2, 3]);
  // using j only to help prevent confusion - could use i
  for (var j = 0; j < fnlist.length; j++) {
    fnlist[j]();
  }
}

testList();

Same question asked here and here. Same answers here, here, here, here and probably in dozen more places.

Community
  • 1
  • 1
pishpish
  • 2,574
  • 15
  • 22