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.
- Something calls
getSomeData
- The engine pushes the return address on the stack
- The engine creates an execution context for the call
- The engine creates a variable environment for the context, populating it with the arguments, local variables, etc.
- 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
- Code exectuion "falls off" the end of the function, so the engine pops the return address from the stack and carries on
- Some time later,
$.get
calls the callback through the reference to it that it kept
- A new context and variable environment are created for the call to the callback
- Callback code runs, looking up things in its own variable environment and also the environment in which it was created (for
$container
)
- Code execution "falls off" the end of the function, the engine pops the stack,
$.get
drops its reference to the function
- Since the function has nothing referring to it, it's eligible for garbage collection
- 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.
- At some point, the engine disposes of the variable environment object
- 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.