0

Learning Javascript and can't figure out why these two functions are different. I saw this example (I added names to the functions):

var txt = ["a","b","c"];

for (var i = 0; i < 3; ++i ) {    
    setTimeout((function myBind(msg) { 
      return function myAlert() { alert(msg); } 
    })(txt[i]), 1000);        
}​

I see that a function that calls alert is being returned. So I thought, why not just return it directly:

var txt = ["a","b","c"];

for (var i = 0; i < 3; ++i ) {    
    setTimeout( function() { alert(txt[i]);} ,1000);        
}​

This ends up alerting 'undefined.' I understand that it's because it's trying to access txt[3] because after one second the loop has finished and i has been set to 3, but I don't understand how the original setup avoided this problem.

Alex
  • 852
  • 1
  • 10
  • 21
  • 1
    Research on closures and how that concept is used to control scope of a variable. Should kick you off in the correct direction. – deostroll Aug 23 '13 at 02:50
  • Common problem; passing a function instead of passing a closure. Also used when passing functions for event handlers. Good reading on closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures – HMR Aug 23 '13 at 04:20

4 Answers4

1

You need to read up about closures. See this answer How do JavaScript closures work? to understand them.

Community
  • 1
  • 1
Ed Heal
  • 59,252
  • 17
  • 87
  • 127
1

Example 1

The closures execute the script instantly and also allow you to pass a parameter inside which is stored and remains stored inside that function. parameter==param

(function(param){})(parameter)

In your example the function returns another function which will be executed by setTimeout.

(function(param){
 return function(){
  alert(param)
 }
})(parameter)

And as you passed the parameter previously to the function it will return the right value.

Example 2

The setTimeout is called 3 times very fast but the parameter is not stored anywhere so it takes the last value know from txt & i and as the loop is ended before the setTimeout is executed the value is 3


basically in the first example you store every txt[i] inside each function you create with those closures.

In the second one you don't store the txt[i]'s anywhere. you just tell the setTimout function to alert txt[i], which after 1second is the last one created by the for loop.

cocco
  • 16,442
  • 7
  • 62
  • 77
  • I don't understand how the msg can live til the end. myAlert is being referenced by setTimeOut, but nothing is referencing myBind. Shouldn't garbage collection pickup myBind...therefore also destroying myAlert? – Alex Aug 23 '13 at 11:45
0

In the first version, txt[i] has been bound to a new variable, msg, which is a different location for each function being created.

In the second version, i is the same location for each of the functions because i's scope is further up; there isn't a new i variable being created each time through the loop.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • so there are 3 separate functions stored in memory? So they look like this: ' function(){ alert('a');} function(){ alert('b')} function(){ alert('c');}' Are those considered dynamic functions then? – Alex Aug 23 '13 at 09:02
  • @Alex There are 3 distinct function objects, right. The _body_ (code) of those function objects are the same, but with `msg` bound to a different object each time. As others have mentioned, these are closures (I guess you could think of closures of "dynamic functions" if it helps you understand them, initially). – C. K. Young Aug 23 '13 at 10:36
0

This is a concurrency problem.

setTimeout creates a "thread" for each execution, but won't evaluate the function until the timeout expires.

so, after a second, when the first timeout expires, your for loop has ended (with i having 3 as value), so accessing the txt[i] returns undefined. Try printing the value of i inside the function with console.log

Juan Guerrero
  • 613
  • 4
  • 8