"Compiled" and "interpreted" describe implementations, not languages, and ECMA-262 only requires that an implementation produce the specified observable results. Real engines are hybrids.
A typical engine pipeline, using V8 as the example:
- Parse. The source becomes an AST. Syntax errors and early errors (such as duplicate
letdeclarations) are reported here; the spec requires a script's early errors at parse time, before any of its code runs. V8 pre-parses inner functions lazily: it checks them for errors but skips building their full AST until first call. - Compile to bytecode. The Ignition interpreter runs compact bytecode. Starting here keeps startup fast and memory low.
- Tier up. While running, the engine collects feedback about types and shapes. Hot functions are compiled to machine code: V8 has Sparkplug (a fast baseline compiler), Maglev (a mid-tier optimizing compiler) and TurboFan (the top-tier optimizer).
- Deoptimize. Optimized code rests on assumptions from that feedback. If one breaks, say a function suddenly receives a different type, the engine discards the machine code and falls back to a lower tier.
So JavaScript is parsed ahead of execution and compiled just in time, but a first call is still interpreted. Other engines use similar tiers with different names.
The interview-safe answer: "It is usually parsed, compiled to bytecode, interpreted, and JIT-compiled for hot paths. It is not an ahead-of-time compiled language, and it is not line-by-line interpreted."