2

I have read number of answers in SO, however I cannot come up a clear explanation in my mind about variable life time in javascript. Generally answers are about hoisting/shadowing which is not the case here imo. For instance, we often write those kinds of scripts along with jQuery;

function getSomeData() {
  var $container = $('#someContainer');
  $.get('/url', function(data) {
    $container.html(data);
  });
}

Now, my main question is how does $container variable remain available to the anonymous callback function? I know, when execution comes up to $.get, get immediately returns and getSomeData returns eventually, though get may return any time in future. Hence I'm forced to think that getSomeData in fact remains in function call stack in order to provide $container variable to callback function, because to my knowledge if a variable is not found in the scope it is used, interpreter looks it up in parent scope (to the global scope). Here are the side questions:

  • Does this kind of pattern causes performance degradation?
  • If I change anonymous function to a declared function and use like;

    var callback = function(data) { $container.html(data); } $.get('/url', callback);

    is there any advantage of this usage (except for debug purposes, and readability)?

  • If there is no function call stack involving here, where is $container stored until the promise resolves? Plase note that also this nesting is possible:

    function getSomeData() { var $container = $('#someContainer'); var replaceHtml = function(data) { $container.html(data); } $.get('/url', function(data) { replaceHtml(data); }); }

Please give a clear explanation of that, thanks!

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
px5x2
  • 1,495
  • 2
  • 14
  • 29
  • Well @Bergi, TJ's explanation is far more better/deeper than yours, actually it's not a duplicate. – px5x2 Nov 26 '15 at 14:41
  • @ px5x2 Thanks, but if Bergi was referring to [*How do JavaScript closures work?*](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work), he was probably right. I'm not sure why, but something about your question didn't make me reach for that, but it really is the crux of the above. In any case, be nice to Bergi, he's a *very* smart, well-informed, and helpful person wrt JavaScript (and probably other things). :-) – T.J. Crowder Nov 26 '15 at 15:00

1 Answers1

2

Now, my main question is how does $container variable is available to anonymous callback function?

...Hence I'm forced to think that getSomeData in fact remains in function call stack in order to provide $container variable to callback function, because to my knowledge if a variable is not found in the scope it is used, interpreter looks it up in parent scope (to the global scope).

You're really close to understanding this. Just a couple of corrections and clarifications will get you there.

It has nothing to do with the function call stack. You're quite right that getSomeData returns before the callback is run. That means it comes off the stack, just like normal. However, its local variables and such live on, even though the function has returned. Why? Because the callback has an indirect reference to them.

When a JavaScript function is called, an object is created (theoretically) which is the execution context for that call. That context contains a variety of things, including an object called the variable environment that contains the arguments and variables and such related to the call. Any function created within the context keeps a reference to that context's variable environment object, and so has access to them even though the function has returned. These functions are called closures because they "close over" the context in which they were created.

So when the callback refers to the $container variable, the JavaScript engine looks in the callback's own context and its variable environment and, not finding $container there, looks at the next containing environment. It finds $container there and uses it.

Let's break it down into steps. Please note there's hand-waving here, the spec contains the details.

  1. Something calls getSomeData
  2. The engine pushes the return address on the stack
  3. The engine creates an execution context for the call
  4. The engine creates a variable environment for the context, populating it with the arguments, local variables, etc.
  5. The engine runs the code in the function
    • Part of that is creating the callback function, which is given a reference to the variable environment object
    • Another part is handing that function reference to $.get, which keeps it for a while
  6. Code exectuion "falls off" the end of the function, so the engine pops the return address from the stack and carries on
  7. Some time later, $.get calls the callback through the reference to it that it kept
  8. A new context and variable environment are created for the call to the callback
  9. Callback code runs, looking up things in its own variable environment and also the environment in which it was created (for $container)
  10. Code execution "falls off" the end of the function, the engine pops the stack, $.get drops its reference to the function
  11. Since the function has nothing referring to it, it's eligible for garbage collection
  12. At some point, the engine's GC algorithm disposes of the function, and so the function no longer refers to the variable environment object from the call to getSomeData, and so that object is eligible for GC.
  13. At some point, the engine disposes of the variable environment object
  14. Now, other than side-effects (like actually using the information from $.get), all is cleaned up

Does this kind of pattern causes performance degradation?

Not in and of itself, no. If you create a function and keep it around, then since it keeps the variable environment where it was created around, that can cause memory impact. But once you release the function (in this case, once $.get has called it and dropped its reference to it), then the function can be GC'd, and the variable environment (and its contents) can be GC'd.

In the above I talk about a lot of objects and garbage collection, but of course JavaScript engines can and do a lot of optimization around this. It's a big part of why modern engines are so much faster than the old ones were. (Just one part, but part.)

If I change anonymous function to a declared function and use like;

var callback = function(data) {
  $container.html(data);
}
$.get('/url', callback);

is there any advantage of this usage (except for debug purposes, and readability)?

No.

If there is no function call stack involving here, where is $container stored until the promise resolves?

See above.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875