JavaScript has traditionally been called an interpreted language because developers usually run source code without manually compiling it first. In a modern engine such as V8, however, the process is more involved: the engine parses the source, builds an abstract syntax tree, produces bytecode, and begins executing that bytecode with an interpreter such as Ignition. Frequently executed code can then be identified as "hot" and compiled at runtime into optimized machine code by the engine's JIT pipeline.
This approach balances startup time and long-term performance. Compiling every function immediately would consume extra time and memory, even though some code might never run. Interpreting first lets the program start quickly, while JIT compilation speeds up code that runs repeatedly.
Is it accurate to keep calling JavaScript an interpreted language, and how should the relationship between interpretation, bytecode, and JIT compilation be understood today?
4 Answers
It’s reasonable to call JavaScript interpreted from a user’s perspective because you normally run the source without an explicit build step. Technically, though, modern engines can interpret bytecode at first and then compile frequently used code into machine code behind the scenes. The language isn’t inherently one or the other; the execution strategy belongs to the engine.
The general flow is close, but the CPU does not execute the interpreter’s instructions as if they were the JavaScript bytecode directly. The interpreter is itself compiled machine code that reads bytecode and performs the requested operations. Later, optimized machine code generated by JIT compilation can run hot functions more directly, avoiding much of that interpretation work.
JavaScript was traditionally labeled interpreted because early engines mainly executed it at runtime rather than producing a compiled executable ahead of time. That description was convenient, but it was never a complete definition of the language itself.
Compiling everything at startup would delay initial execution and waste resources on functions that are never called. Starting with bytecode and interpretation provides fast startup, while compiling only frequently used code improves performance where it matters. That startup-versus-running-speed tradeoff is the main reason engines use a tiered approach.

That makes sense—the distinction is less about JavaScript being permanently interpreted or compiled and more about which strategy the engine chooses at each stage of execution.