"V8 isn't an interpreter. It's a JIT compiler that pretends to be one until your code gets hot."
What V8 Actually Does
V8 takes JavaScript source — a string of characters you wrote — and turns it into something a CPU can execute. The naive way is to interpret it line by line, like Python's CPython has historically done. The fast way is to compile it to machine code. V8 does both, in stages, and the staging is what makes it fast.
The pipeline (as of V8 12.x bundled with Node 26) looks roughly like this: parser → AST → Ignition (interpreter, emits bytecode) → TurboFan (optimizing JIT, emits machine code) → Sparkplug + Maglev (intermediate tiers added 2021-2023). When you first start your script, V8 doesn't waste time optimizing — it parses and runs through Ignition. As the same code paths heat up (called many times with similar inputs), V8 promotes them to higher tiers. The hottest paths end up as real x86_64 / arm64 instructions.
This is why JavaScript benchmarks are so weird: the first run is slow, the tenth is fast, and a small code change can knock you off the optimized tier back into the interpreter. "My code got slower after I added a parameter" is a real V8 conversation.
Hidden Classes — The Trick That Made JS Fast
JavaScript objects look like dictionaries: {x: 1, y: 2}. A naive engine treats them as hash maps and pays a hash lookup on every property access. V8 doesn't. V8 watches the *shape* of your objects and creates a *hidden class* for each shape behind your back. Two objects with the same keys, in the same order, share a hidden class. Property access becomes a single memory offset — almost as fast as a C struct.
The catch: if you add or delete properties dynamically, or assign properties in different orders, V8 has to invalidate the hidden class. That kills the optimization. This is why the advice "always initialize all your object properties in the constructor in the same order" exists — it lets V8 keep the same hidden class for every instance.
V8 ≠ Node
readFile or http.get, V8 hands control back to Node's C++ code, which calls libuv, which calls the OS. The boundary between "V8 land" and "Node land" is real — and it matters for performance debugging.Other Engines You'll See Mentioned
V8 isn't the only JavaScript engine. Firefox uses SpiderMonkey. Safari uses JavaScriptCore (also called Nitro). Bun uses JavaScriptCore — that's part of why Bun starts up faster than Node (different engine startup characteristics). Deno uses V8 — same engine as Node, different runtime around it.
You don't need to know SpiderMonkey or JavaScriptCore internals to write Node. But knowing they exist explains why "this code is faster in Bun" or "this works in Chrome but not Safari" can happen — same language, different engine implementing it.