The JavaScript Engine
Introduction
A JavaScript engine is a program that reads your source code and executes it. Every browser ships one — V8 (Chrome/Edge), SpiderMonkey (Firefox), JavaScriptCore (Safari) — and Node.js embeds V8.
The engine is not the whole runtime: it executes the language, but features like setTimeout, fetch, and the DOM come from the surrounding host environment.
Why This Matters
Once you can picture the parse → compile → execute pipeline, previously "magic" behaviours become obvious: hoisting falls out of the parsing phase, JIT optimisation explains why hot loops speed up, and you can reason about performance instead of guessing.
Theory
What the engine does
- Parsing — the source text is tokenised and turned into an Abstract Syntax Tree (AST). Syntax errors are caught here, before any code runs.
- Compilation — a baseline compiler turns the AST into bytecode (V8's interpreter is called Ignition).
- Execution — the bytecode runs. Meanwhile a profiler watches which functions run often ("hot").
- Optimisation (JIT) — hot functions are recompiled to highly optimised machine code by an optimising compiler (V8's TurboFan). If an assumption breaks (e.g. a variable's type changes), the engine deoptimises back to bytecode.
Core components
- Memory Heap — where objects and closures are allocated.
- Call Stack — where function frames are pushed/popped as code runs.
- Garbage Collector — reclaims memory that is no longer reachable.
Key insight: JavaScript is compiled, then interpreted, then re-compiled
Modern engines blur the line between "interpreted" and "compiled". Your code is compiled to bytecode up front and the hottest parts are JIT-compiled to native code at runtime.
Visual Diagrams
Source code
|
v
[ Parser ] --> AST --> [ Interpreter (Ignition) ] --> Bytecode --> Execute
| ^
| hot function? |
v |
[ Optimising compiler (TurboFan) ] -------+
| (deoptimise if assumption breaks)
v
Optimised machine codeV8's two-tier design: fast startup via bytecode, peak speed via JIT.
Interview Questions
1Walk me through what happens when a JS engine runs a file.
The engine parses the source into tokens and an AST (catching syntax errors). A baseline compiler turns the AST into bytecode, which an interpreter executes. A profiler marks frequently-run ("hot") functions, and an optimising JIT compiler recompiles them to native machine code. If a runtime assumption is invalidated, the engine deoptimises back to bytecode. Throughout, objects live in the heap, execution uses the call stack, and a garbage collector frees unreachable memory.
Follow-ups
- What is deoptimisation and what triggers it?
- Where do closures live — stack or heap?
2Is JavaScript interpreted or compiled?
Both, in modern engines. Code is compiled to bytecode ahead of execution and interpreted, then hot paths are JIT-compiled to machine code at runtime. Calling JS purely "interpreted" is outdated.
Quiz
1. In V8, which component turns hot bytecode into machine code?
2. Where are objects and closures allocated?
Summary
- An engine parses source into an AST, compiles it to bytecode, and executes it.
- A JIT compiler recompiles hot functions to native code and can deoptimise them.
- The engine provides the heap, the call stack, and the garbage collector.
- V8 (Chrome/Node), SpiderMonkey (Firefox), and JavaScriptCore (Safari) are the major engines.