Jaeeun Lee Jaeeun Lee - 4 months ago 15
Javascript Question

Closure Inside For Loop

I know that one of the ways to log 0 to 9 with this code:

EDIT: Source

for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}), 10)
}


jsfiddle

is to make
setTimeout
self invoking and pass
i
as a parameter, like so:

for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 10)
}


but I've tested making
setTImeout
self invoking without passing
i
, and it still works:

for(var i = 0; i < 10; i++) {
setTimeout((function() {
console.log(i);
})(), 10)
}


jsfiddle

My questions:


  1. Why does it work even without passing
    i
    as a parameter?

  2. Is it necessary to pass
    i
    ?


Answer

Problem

It's not a Closure.

for(var i = 0; i < 10; i++) {
  setTimeout((function(i) {
    console.log(i);
  })(i), 10)
}

setTimeout expects actually a function as parameter, but you made it invoking immediately. So it logs immediately the value of i, without waiting. setTimeout has now the return of your anonymous function as first parameter which is undefined.

The same here:

for(var i = 0; i < 10; i++) {
   setTimeout((function() {
     console.log(i);
   })(), 10)
}

It's executed immediately, it doesn't wait for the 10ms. The only difference you didn't passed i as parameter, but it will look in the parent scope for a variable called i – and there is one.

You will see that your anonymous function is called immediately if you set the time to e.g. one second (1000).

Solution

A real closure would look like this:

Without parameter: You will see 10 times 10, because at time of execution of the inner function the loop is already finished which means i equals 10 at that time:

for(var i = 0; i < 10; i++) {
   (function() {
        setTimeout(function() {
         console.log(i);
      },10);
   })();
}

With parameter – you will get the expected result:

for(var i = 0; i < 10; i++) {
   (function(i) {
        setTimeout(function() {
         console.log(i);
      },10);
   })(i);
}