Compilation vs Interpretation (JIT)
Introduction
Is JavaScript compiled or interpreted? The honest answer is both. Modern engines use Just-In-Time (JIT) compilation: they interpret bytecode for fast startup and compile hot code to native machine code for speed.
Understanding this removes a common misconception and explains why benchmarks "warm up".
Why This Matters
Saying "JavaScript is interpreted" in a senior interview is a red flag. Explaining the JIT pipeline — and phenomena like warm-up and deoptimisation — shows real depth.
Theory
The classic definitions
- Interpreter — reads and executes code line by line. Fast to start, slower to run.
- Compiler (AOT) — translates the whole program to machine code before running. Slow to start, fast to run.
JIT: the best of both
A Just-In-Time compiler combines them:
- Code is parsed to an AST and compiled to bytecode, which an interpreter runs immediately (fast startup).
- A profiler counts how often each function runs and records the types it sees.
- Hot functions are recompiled to optimised machine code using those type assumptions.
- If an assumption is violated later (a "number" argument suddenly is a string), the engine deoptimises — throws away the optimised code and falls back to bytecode.
Consequences you can observe
- Warm-up: the same loop gets faster after running many times as the JIT kicks in.
- Deopt cost: type-unstable code (changing shapes/types) can be slower than stable code because it keeps deoptimising.
- Monomorphism wins: functions that always see the same types are the easiest to optimise.
Visual Diagrams
Interpreter : source --> run line by line (fast start, slow run)
AOT compiler: source --> machine code --> run (slow start, fast run)
JIT (JS) : source --> bytecode --> run (fast start)
+ profile hot code
--> optimise to machine code (fast run)
--> deopt if assumptions breakJIT gives fast startup and, for hot code, near-native speed.
Output Prediction
Predict the output #1
1function add(a, b) {2 return a + b;3}4 5// Stable types: the JIT can optimise this well6let total = 0;7for (let i = 0; i < 5; i++) {8 total = add(total, i);9}10console.log(total);Interview Questions
1Is JavaScript compiled or interpreted?
Both. Modern engines use JIT compilation: source is compiled to bytecode and interpreted for fast startup, then hot functions are compiled to optimised machine code at runtime using type feedback, with deoptimisation as a safety net. Describing JS as purely interpreted is inaccurate for engines like V8.
2What is deoptimisation and how do I avoid triggering it?
Deoptimisation is when the engine discards optimised machine code because a runtime assumption (usually about types or object shape) was violated, falling back to slower bytecode. Avoid it by keeping functions monomorphic — pass consistent types, keep object shapes stable, and don't mix element types in hot arrays.
Quiz
1. What best describes JIT compilation?
Summary
- Modern JavaScript uses JIT compilation — it is both interpreted and compiled.
- Bytecode gives fast startup; hot functions are JIT-compiled to machine code.
- The engine deoptimises when runtime type/shape assumptions are violated.
- Type-stable, monomorphic code is the easiest for the JIT to optimise.