1

I was asked the below question during an interview, and I still couldn't get my head around it, so I'd like to seek your advice.

Here's the question:

var countFunctions = [];
for(var i = 0; i < 3; i++){
  countFunctions[i] = function() {
    document.getElementById('someId').innerHTML = 'count' + i;
  };
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();

When asked what would be the output of the above, I said count0,count1 and count2 respectively. Apparently the answer was wrong, and that the output should all be count3, because of the concept of closures (which I wasn't aware of then). So I went through this article and realized that I should be using closure to make this work, like:

var countFunctions = [];
function setInner(i)  {
    return function(){
    document.getElementById('someId').innerHTML = 'count' + i;  
    };
}
for(var i = 0; i < 3; i++){
  countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2

Now that's all well and good, but I remember the interviewer using something simpler, using a self-executing function like this:

var countFunctions = [];
for(var i = 0; i < 3; i++) {
    countFunctions[i] = (function(){
    document.getElementById('someId').innerHTML = 'count' + i;  
    })(i);
}

The way I understand the above code, we are skipping the declaration of a separate function and simply calling and executing the function within the for loop.

But when I ran the below:

countFunctions[0];
countFunctions[1];
countFunctions[2];

It didn't work, with all the output being stuck at count2.

So I tried to do the below instead:

for(var i = 0; i < 3; i++) {
    countFunctions[i] = function(){
    document.getElementById('someId').innerHTML = 'count' + i;  
    };
}

, and then running countFunctions[0](), countFunctions[1]() and countFunctions[2](), but it didn't work. The output is now being stuck at count3.

Now I really don't get it. I was simply using the same line of code as setInner(). So I don't see why this doesn't work. As a matter of fact, I could have just stick to the setInner kind of code structure, which does work, and is more comprehensive. But then I'd really like to know how the interviewer did it, so as to understand this topic a little better.

Deepak Ingole
  • 14,912
  • 10
  • 47
  • 79
anthonytwp
  • 237
  • 1
  • 3
  • 9

2 Answers2

1

The relevant articles to read here are JavaScript closure inside loops – simple practical example and http://benalman.com/news/2010/11/immediately-invoked-function-expression/ (though you seem to have understood IEFEs quite well - as you say, they're "skipping the declaration of a separate function and simply calling and executing the function").

What you didn't notice is that setInner does, when called, return the closure function:

function setInner(i) {
    return function() {
        document.getElementById('someId').innerHTML = 'count' + i;  
    };
}

// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML

So if you translate it into an IEFE, you still need to create (and return) the function that will actually get assigned to countFunctions[i]:

var countFunctions = [];
for(var i = 0; i < 3; i++) {
    countFunctions[i] = (function(i){
        return function() {
            document.getElementById('someId').innerHTML = 'count' + i;
        };
    })(i);
}

Now, typeof countFunctions[0] will be "function", not "undefined" as in your code, and you can actually call them.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Hi, thanks a lot for your feedback. I've tried out your approach, so that countFunctions[0] is now a function and can be called. The problem, however, is that the "i" seems to be stucked at 3, so that no matter what I call - callFunctions[0], callFunctions[1], or callFunctions[2], the result is still count3. See http://jsfiddle.net/r5Nnp/ – anthonytwp May 19 '14 at 07:27
  • Thanks for the hint, I've forgotten the parameter of the the IEFE so that no closure was built… See edit. – Bergi May 19 '14 at 07:29
0

Take a look at these four functions:

var argument = 'G';                      //global

function passArgument(argument){
    alert(argument);                     //local
}

function noArguments(){
    alert(argument);                     //global
}

function createClosure_1(argument){
    return  function (){
                alert(argument);         //local
            };
}

function createClosure_2(argument){
    var argument = argument;             //local
    return  function (){
                alert(argument);         //local
            };
}

passArgument('L');                       //L
noArguments();                           //G
createClosure_1('L')                     //L
createClosure_2('L')                     //L
alert(argument)                          //G
  1. I think, first function is obvious.
  2. In function noArguments you reference the global argument value;
  3. The third and fourth functions do the same thing. They create a local argument variable that doesn't change inside them and return a function that references that local variable.

    So, what was in the first and the last code snippet of your question is a creation of many functions like noArguments, that reference global variable i.

    In the second snippet your setInner works like createClosure_1. Within your loop you create three closures, three local variables inside them. And when you call functions inside countFunctions, they get the value of the local variable that was created inside the closure when they were created.

    In the third one you assign the result of the execution of those functions to array elements, which is undefined because they don't return anything from that functions.

Vlas Bashynskyi
  • 1,886
  • 2
  • 16
  • 25