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)?
You can see that the functions
save
andvalidate
are defined after the function returned a value.
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. Since save
and validate
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 the return
.
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 on env
(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 to bindings
var
to bindings
(if not already defined) with the value undefined
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.
Is it standard-compliant and works in all browsers (say, from IE 6)?
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:
doSomething();
function doSomething() {
// ....
}
...and people do that all the time.
This "hoisting" is one of the key differences between function declarations and function expressions. If save
and validate
were created by function expressions, then it would matter a great deal that they were written after the return
— they'd never get created at all:
// It wouldn't work like this, for instance
function dataService() {
var someValue = '';
var service = {
save: save, // `save` has the value `undefined` at this point
someValue: someValue,
validate: validate // So does `validate`
};
return service;
////////////
var save = function() { // Now this is a function expression
/* */
};
var validate = function() { // This too
/* */
};
}
The save
and validate
variables would get created (thanks to Step 9 in the above), but as of where they're used, they'd have the value undefined
and so the returned object wouldn't be useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With