The line between "interpreted" and "VM" is, well... often meaningless if you're talking things like the JVM and/or P-Code. Execution of JavaScript (and PHP works in a similar fashion) is handled by a two stage model, first the code is parsed into an intermediary byte-code, and then the interpreter runs the bytecode.
It is during the byte-code generation stage that hoisting occurs as "labels" (function names, variable names, and so forth) have pointers allocated, something made a bit easier in JavaScript by the fact that deep down everything is an object, so you just create a pointer to a memory block of the size required by the function/variable/whatHaveYou.
That's why whenever they call things like .NET and JAVA "Virtual Machines" I cry BULL-***ING-SHIT! They are interpreters, nothing more nothing less. Compiling to an intermediate bytecode doesn't change that -- if it did most every major ROM Basic from the late '70's and early '80's would in fact be "Virtual Machines" -- when they quite clearly are not.
Hell, by default most ROM Basic's don't even store the code verbosely in RAM as you type it in. They symbols are tokenized whenever you enter a line, and de-tokenized when you run 'list'. That's why default most ROM Basic files -- like GW-BASIC's .bas files -- are binary gibberish anyplace that isn't a string or comment! You have to save the file with a special parameter to force saving as legible ASCII.
In any case, that's what JavaScript engines do. The Engines turn it into an intermediate bytecode first before it even tries to run it. Since the "namespace list" is just an array of pointers with a few flags thrown on top, when an unknown token is encountered it is stored on the list with a null pointer. When it is actually defined that pointer structure is overwritten with the proper value -- and boom, it works even though you defined it after you call it.
If you are actually interested in these types of internals, you might want to go back to the "real source" of pretty much all compiler design. Whilst most 'interpreted' languages aren't properly compiled to native code, more often than not they ARE compiled to an intermediate bytecode -- which is what mostly makes .NET and JAVA nothing 'special' despite the wild claims to the contrary... and that means going to the best (though really old) source about building compilers:
The less code you use, the less there is to break