Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A better understanding of javascript precompile

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!

like image 859
Rico Avatar asked Apr 21 '15 11:04

Rico


3 Answers

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
like image 186
CodingIntrigue Avatar answered Nov 11 '22 00:11

CodingIntrigue


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:

  1. 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.

  2. 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:

  1. Creates a variable called foo and gives it the initial value undefined.

  2. 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.

  3. Starts step-by-step code for that scope.

  4. Assigns the value 1 to foo.

  5. Calls the bar functon.

  6. 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.

  7. Starts the step-by-step code for that scope.

  8. Assigns the value 10 to the local foo (which used to refer to the function).

  9. Returns out of the function.

  10. 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.

like image 38
T.J. Crowder Avatar answered Nov 11 '22 00:11

T.J. Crowder


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.

like image 1
deceze Avatar answered Nov 11 '22 02:11

deceze