1

so I'm understanding closures and circular references (I hope), but one aspect is the much reported closure within a loop which seems to cause much ado. I need clarification on it. The code I'm looking at is:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function getHelp(help) {
  alert(help); 
  // only to show value of "help" at this point, also to prove that "getHelp()" is being called even
  // though the onfocus wasn't used

  return function() {
    showHelp(help);
  }
}

function setupHelp(){
  var helpText = [
    {'id':"email",'help':"Your email address"},
    {'id':"name",'help':"Your full name"},
    {'id':"age",'help':"Your real age"}
  ];

  for(var i=0;i<helpText.length;i++){
    var item = helpText[i];
    alert(item.help);
    // only to show value of "help" at this point, also to prove that "getHelp()" is
    // being called even though the onfocus wasn't used
    document.getElementById(item.id).onfocus = getHelp(item.help);
  }
}

The premise here being that you have three input fields and focusing them will return a helpful hint (as seen on the MDC article). But, here's the crux of my lack of grasp: If you put an alert (so as to test) in the "getHelp()" function, and put one in the "setupHelp()" function, just before you set bind the event handler to a reference to the dom element, you'll see that, on loading the page, the loop runs, then the getHelp() function, then the loop, then the getHelp() function, etc, until the end of the loop. So if the getHelp() function is bound to the onfocus event handler, and you don't even focus the input fields, howe can the getHelp() function run?? And how does JAvaScript store all possible outcomes from that one little loop? :S

Any help you could provide would really help, this is mind boggling right now. I'm sure it'll just click one of these days, but I'm impatient. :P

Tom.

ide
  • 19,942
  • 5
  • 64
  • 106
Tom
  • 3,272
  • 3
  • 16
  • 13
  • http://stackoverflow.com/questions/1451009/javascript-infamous-loop-problem and http://stackoverflow.com/questions/643542/doesnt-javascript-support-closures-with-local-variables – ide Feb 28 '11 at 22:27

2 Answers2

1

So if the getHelp() function is bound to the onfocus event handler, and you don't even focus the input fields, howe can the getHelp() function run?? And how does JAvaScript store all possible outcomes from that one little loop?

This is the crux of your question, and the answer lies here:

for(var i=0;i<helpText.length;i++){
    var item = helpText[i];
    alert(item.help);
    // only to show value of "help" at this point, also to prove that "getHelp()" is
    // being called even though the onfocus wasn't used
    document.getElementById(item.id).onfocus = getHelp(item.help);
}

That's how the JavaScript interpreter stores all of the possible outcomes: Because you told it what they were. You're calling getHelp, which is generating a function, and then returning that function.

How this works is much simpler than it seems. :-) I go into it a fair bit here, but basically: When you call a function, something called an execution context is created. That's an object (JavaScript is massively object-oriented, right down to the interpreter level). In that execution context object, there's something called the variable object. It holds all of the variables for the execution context, as properties. This includes the arguments to the function, all the vars within the function, and any declared functions as well (you don't have any declared functions in your example, so we can ignore that; you have only function expressions, which is fine). Any function declared or defined by expression within an execution context has an enduring reference to the variable object for that execution context, and uses it to resolve variable references when it's called.

So: In your loop, when you call getHelp, an object is created storing the data related to that call. That object is bound to the function that you're creating within the call and storing on the onfocus handler (that's the data that the function closes over [which is why it's called a closure]). When/if the handler is called, that's how the references that function holds are resolved, against properties on that object.

More reading: Closures are not complicated

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks for your answer, for putting that time in. But let me clarify deeper still, if I might. Are you saying that, when the main function runs, a new execution context is created for each run of the loop, tying it up to the variables and function and event handlers, just waiting for the user to focus so it can spring into action? Still strange that the "actual" getHelp() function runs, even before the onfocus... Or maybe it's only running so as to store it in a seperate execution context? ... Maybe? :D – Tom Feb 28 '11 at 22:45
  • @Tom: When your loop runs, you create several functions, and each has an execution context bound to it. Then you assign each of those functions to an event handlers, so yes, the functions are sitting there waiting for the event to occur. If you didn't keep a reference to the functions, they (and the execution contexts and their associated variable objects) would be eligible for garbage collection becuase nothing was referring to them. But since you keep the reference, that keeps things alive. – T.J. Crowder Feb 28 '11 at 22:48
  • @Tom: *"Still strange that the "actual" getHelp() function runs, even before the onfocus..."* It's not strange at all, you're explicitly calling that function in your loop that sets up the event handlers. Why would it be strange that a function you're calling is getting called? You've lost me... – T.J. Crowder Feb 28 '11 at 22:49
  • Well, I'm used to saying something like someElem.onfocus = function(){alert("Hello, T.J");} And then having to actually perform the focus to call the function ... So the fact that this is getting called instantly was a little puzzling. But I have a feeling that this is getting called immediately so as to evaluate the showHelp() function ready for using when the user focuses the elements. But I might be totally wrong. I need a good book on this. The JavaScript Bible (and various online articles don't do very good job). :( – Tom Feb 28 '11 at 22:58
  • Just went to your article. Gonna read that and see if it helps. You've been way helpful so far. :) Shall post a comment soon. :D Thanks again! – Tom Feb 28 '11 at 23:00
  • @Tom: But the point is that *you're* calling `getHelp`. In your loop. That's not the result of an event, it's part of your loop logic, exactly like calling `alert` at that point would be. Whereas your example of `someElem.onfocus = function(){alert("Hello, T.J");}` is *defining*, but not calling, a function at the point where it runs. The difference is whether you use the "call" operator (`(...)`) after the function reference. – T.J. Crowder Feb 28 '11 at 23:00
  • @Tom: Glad this is helpful. (Upvoting helpful answers is...helpful.) – T.J. Crowder Feb 28 '11 at 23:02
  • Ahh, ok, my bad. Being relatively new to JS I wasn't aware you could call an event immediately within a function. :) I am but a lowly Padawan. :P – Tom Feb 28 '11 at 23:03
  • @Tom: You're not calling the event. :-) Your line, `document.getElementById(item.id).onfocus = getHelp(item.help);`, calls the `getHelp` function and passes in the value `item.help`. Right then and there, no event required. So the `alert` within `getHelp` executes immediately, because you called the function. Then `getHelp` creates a new (anonymous) function and returns it, and that function is assigned to the `onfocus` handler. It (the anonymous function created by the call to `getHelp`) isn't called until/unless the `focus` event occurs. – T.J. Crowder Feb 28 '11 at 23:05
  • Ahh, no wait! One more quick question then I swear I'll stop pestering. :D So, so, so, I am calling the onfocus immediately, which goes up to getHelp() creates a new anonymous function, returns it, creates an execution context for that particular instance, and waits until the element is clicked... Please god tell me I'm on the right track! :P – Tom Feb 28 '11 at 23:08
  • @Tom: I'm afraid not. :-) You're never calling the function you end up assigning to `onfocus` (it will get called if the event occurs, though). The line `document.getElementById(item.id).onfocus = getHelp(item.help);` breaks down like this: Execute `getHelp(item.help)` and assign the *result* of that function to `document.getElementById(item.id).onfocus`. The result of your `getHelp` function is, itself, a function (**not** `getHelp`) that is generated by `getHelp` on each call. – T.J. Crowder Feb 28 '11 at 23:10
  • Yeah, it's like a "function factory" you can't see. :P – Tom Feb 28 '11 at 23:12
  • @Tom: **Exactly**, `getHelp` is a function factory. (But you *can* see it, `getHelp` returns the result of a function expression. `return function() { ... };` is exactly like `return new Foo(...);`, it creates something and returns it.) – T.J. Crowder Feb 28 '11 at 23:16
  • Dude, seriously get a Twitter!! :D – Tom Feb 28 '11 at 23:20
  • @Tom: LOL I do have one (tjcrowder), I just never tweet. The links above are to my blog, though. On those rare occasions when I have something worth saying, that's where I tend to say it. (Well, and here.) – T.J. Crowder Mar 01 '11 at 08:22
0

actually, you're assigning the result of getHelp(help) to your onfocus event, so it's totally normal that both alerts shows, since getHelp is executed to give its result.

If you put the alert in the returner function, I'm pretty sure you won't see it :

function getHelp(help){
    return function(){
        alert(help); 
        showHelp(help);
    }
}
krtek
  • 26,334
  • 5
  • 56
  • 84
  • You're right, putting the alert in the returner doesn't execute... Which leads me to believe that the outer "getHelp()" function is only evaluating the inner function for use later on, setting it in its own "execution context", as Master Crowder aptly pointed out. :) – Tom Feb 28 '11 at 22:49