0

As a javascript beginner, I thought I realized the Closure feature until seeing the trap from the sample code below. I tried to modify the code to pop up the result which many C/C++/C#/Java programmers expect as,

"item1 1"

"item2 2"

"item3 3"

After one hour struggling, I still can't do that. Could any javascript master teach me how to modify it ? Thank you.

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();
Community
  • 1
  • 1
Jeff T.
  • 2,193
  • 27
  • 32

2 Answers2

1

The idea is to create a closure inside which it will be fixed.

  1. You can nest the for content inside an immediate function :

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

function testList() {
    var fnlist = buildList([1,2,3]);
    for (var j = 0; j < fnlist.length; j++) fnlist[j]();
}

testList();

It is the same as writing an other function :

function doStuff(k,arr,arr2){
    var item = 'item' + arr[k];
    arr2.push( function() {alert(item + ' ' + arr[k])});    
}

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) doStuff(i,list,result);
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    for (var j = 0; j < fnlist.length; j++) fnlist[j]();
}

testList();
  1. With the recent forEach(value, index, array) method the closure is automatically provided :

function buildList(list) {
    var result=[];
    list.forEach(function(e){
        result.push(function(){alert('item'+e+' '+e)});
    });
    return result;
}

buildList([1,2,3]).forEach(function(e){e();});

Rather cool.

  1. Using ECMAScript 6 let (thanks elad.chen)

let can replace var to scope i inside functions in the loop : no more need to create a closure as in (1) and (2). You will just have to move the item+list[i] part inside the function. However let is available in strict mode only, and not in all browsers yet (it still misses firefox for example)

'use strict'

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

function testList() {
    var fnlist = buildList([1,2,3]);
    for (var j = 0; j < fnlist.length; j++) fnlist[j]();
}

testList();
Mouloud85
  • 3,826
  • 5
  • 22
  • 42
1

Not master by any means, just learning, too:

   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]);

}
testList();

This could be good chance for IIFE usage. Demo: http://jsfiddle.net/zh02a69j/2/

In the moment when you call your first function by fnlist[j](), your i value is last incremented value. Your function which push results to array 'see' just that value, since it is not executed immediately, in loop - it is defined, but not executed. You can make it execute immediatelly (as IIFE), in loop, then script returns expected output.

If i made some mistake (to all previous and future downvoters:)) - please guide me, too.

sinisake
  • 11,240
  • 2
  • 19
  • 27
  • 1
    Hello @nevermind, I inspected your code snippet for a while. I found that only the first line is needed in the testList() function to get the correct answer while the rest of code in the testList() would take no effect. This way could make me harder to realize the essence of function scope in js, though I like your clever answer :) – Jeff T. Sep 20 '15 at 04:25
  • Yes, you are right. Let's edit answer one more time... – sinisake Sep 20 '15 at 09:05