var foo=1;
function bar(){
foo=10;
return;
function foo(){}
}
bar();
alert(foo);
I am currently learning on how javascript is actually running in the machine and this is a piece of code I see in the example. I got no idea why the final alert is 1 instead of 10. So I am wondering could any one help me explain how the javascript virtual machine is actually execute these code. Thanks!
This is due to function declaration hoisting:
var foo=1;
function bar(){
function foo(){} // This gets moved up here by the engine
foo=10; // You've reassigned the local `foo` function to 10,
// leaving the global `foo` untouched
return;
}
bar();
alert(foo); // Since the foo has never changed in this scope, it's still 1
I got no idea why the final alert is 1 instead of 10.
Because the foo
in this line in bar
:
foo = 10;
...is the variable-like thing* declared by the function declaration later on in that function:
function foo(){}
...not the foo
that's outside bar
. That is:
var foo=1;
function bar(){
foo=10; // <== This `foo`
return;
function foo(){} // <== Is the `foo` declared here
}
bar();
alert(foo);
...not to the foo
declared in the containing scope (var foo
).
There are two reasons that's happening:
Function declarations are processed immediately on entry to the containing scope (the call to bar
, in this case), prior to any step-by-step code in the function. This is sometimes called "hoisting" the declarations (because they happen as though they were at the very top). And since the function declaration isn't step-by-step code, the return
has no effect on whether it gets processed; it gets processed before the return
ever happens.
Function declarations also create what might as well be variables with the names of the functions. So the foo
in the function declaration effectively becomes a variable with that name (more below) — and as you've seen in that code, you can assign new values to those "variables."
When you run that code, here's the order of things that the JavaScript engine does:
Creates a variable called foo
and gives it the initial value undefined
.
Creates the function bar
, adding bar
as an in-scope symbol (effectively a variable) in the current scope and making it a reference to the bar
function.
Starts step-by-step code for that scope.
Assigns the value 1
to foo
.
Calls the bar
functon.
Creates the foo
function relevant to that call to bar
, adding foo
as an in-scope symbol (effectively a variable) during the call and making it a reference to the function.
Starts the step-by-step code for that scope.
Assigns the value 10
to the local foo
(which used to refer to the function).
Returns out of the function.
Calls alert
with the foo
in that scope, which still has the value 1
.
You can read up on all the gory details in §10.4.3 of the spec and the sections it links to.
* "variable-like thing" In JavaScript, each execution context (the global context and any contexts created by calling functions, etc.) has an object that it uses to hold various names used in that context and their values; it's called a "binding object." The binding object for a context (I'm skipping some unrelated details here) has properties for each variable, function declaration, and a few other things like the arguments
pseudo-array, the name of the function itself (referring back to the function), and such. The name of the properties are the names of the variables, declared functions, etc. That's why assigning to foo
inside bar
overwrites the reference to the foo
function declared in bar
, instead of assigning to the variable in the outer scope. foo
is effectively a local variable in bar
, even though it's not declared with var
, because of the function declaration.
This has to do with a concept called hoisting. function foo
is essentially just an alternative syntax for var foo = function ..
, so inside bar
the name foo
does not refer to the outer foo
variable but to a locally defined foo
. This foo
is first a function, but later gets overwritten by 10
.
Now, through hoisting the name foo
is "reserved" and scoped at parse time, before the code executes. Essentially, it executes like this:
function bar(){
var foo = function () {};
foo = 10;
return;
}
Hence it does not overwrite the outer variable at all.
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