Well, in the first place, MDN clearly says that JavaScript is an interpreted language (it also says JIT-compiled which I will address later in the article). Still there is a question that if JavaScript is really interpreted because of the following points.
Take a look at the following schematic of Google's V8:

The code is parsed first, which means it is translated into tokens, which form an abstract syntax tree. That tree is fed to an interpreter, which can do stuff, like hoisting and in general will provide an un-optimized JIT compilation (because that's really quick!). While the code runs, it is analysed and especially often executed code is sent to TurboFan for an optimized compilation. The un-optimized code is then replaced by optimized code. It's one of many tricks to get the fastest JS experience into the browser.
Firefox does something similar in the latest version for WASM (however, regular JS just needs the additional parser step). They released a pretty interesting read on it, which might help you further understand modern JS code interpretation and compilation ;)
JavaScript support both. When first time JavaScript programme write then its goes to compile phase.
In compile phase the main purpose is making execution context. In this context compiler make global execution context and context for each function and the memory allocation for variable in stack those are possible( here hosting concept is implement).
After that code is moving forward for execution, and for that JavaScript used interpreter. Now interpreter execute each code line by line and make decision which is going to insert in execution context and which "function execution context" push into execution context and when when to pop item from execution context.
So easy way to remember is compiler used for make call stack/ execution context and interpreter for perform operation on call stack by reading code.
Jason Knight
The less code you use, the less there is to break
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:
github.com/tpn/pdfs/blob/master/Compiler%20Constr…).pdf