I'm currently reading John Papa's AngularJS style guide and saw the code:
function dataService() {
var someValue = '';
var service = {
save: save,
someValue: someValue,
validate: validate
};
return service;
////////////
function save() {
/* */
};
function validate() {
/* */
};
}
You can see that the functions save
and validate
are defined after the function returned a value. How does this work? Is it standard-compliant and works in all browsers (say, from IE 6)?
That's what it looks like from where they're written, yes, but in fact they're defined before any step-by-step code in the function runs at all. Sometimes this is called "hoisting" the declarations to the top of the function (something similar happens with
var
, too; more below).When control enters an execution context (e.g., when you enter a function, enter the global environment at the beginning of the program, or enter
eval
code), one of the several things that happens before any step-by-step code is executed is that all of the function declarations in that context are processed and those functions are created. Sincesave
andvalidate
are defined by function declarations, they're created before the first step-by-step line of the code runs, and so it doesn't matter that they're after thereturn
.Here's what the JavaScript engine does when you call a function (e.g., when calling
dataService
), with the function declarations step highlighted:this
env
) for the call[[Scope]]
property onenv
(this is part of how closures work)bindings
) for the environment to hold our the various names defined by the function (this is another part of how closures work, and also how variable references are resolved)bindings
as a property referring to the functionbindings
bindings
arguments
object, add it tobindings
var
tobindings
(if not already defined) with the valueundefined
This is laid out in excruciating detail in the spec in §10.4.1 and the sections it links to. (If you go to read that, brace yourself, the prose is...turgid...) That's a link to the current spec, but this was clearly laid out in §10 of the old third edition spec in 1999 as well, and I'm fairly sure it's been true right from the beginning.
Yes. It used to make me nervous, so several years back (probably ~2005) I proved it to myself on all of the then-current and not-quite-dead browsers I could find (including IE6), and it was universally handled correctly. Which isn't actually surprising, because it's what makes this code work:
...and people do that all the time.
This "hoisting" is one of the key differences between function declarations and function expressions. If
save
andvalidate
were created by function expressions, then it would matter a great deal that they were written after thereturn
— they'd never get created at all:The
save
andvalidate
variables would get created (thanks to Step 9 in the above), but as of where they're used, they'd have the valueundefined
and so the returned object wouldn't be useful.