Chapter 5: Node.js V8 Engine
Node.js. V8 Engine in the context of Node.js — written as if I’m sitting next to you, drawing on a whiteboard, showing code examples, and explaining why this actually matters when you write Node.js code.
Let’s go step by step — no rushing.
1. The most important sentence first
V8 is the JavaScript engine that actually runs your JavaScript code — both in Chrome and in Node.js.
V8 is not Node.js. V8 is not the event loop. V8 is not the file system or http module.
V8 is only responsible for two things:
- Parsing JavaScript code into something the computer can understand
- Executing that code very quickly
Everything else in Node.js (files, network, timers, streams, child processes…) is added by Node.js on top of V8.
2. Where does V8 live in the Node.js world?
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Browser (Chrome) Node.js ┌─────────────────────┐ ┌─────────────────────┐ │ JavaScript │ │ JavaScript │ │ code │ │ code │ └──────────┬──────────┘ └──────────┬──────────┘ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ V8 │ │ V8 │ ← same engine! └──────┬──────┘ └──────┬──────┘ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────────────┐ │ Browser │ │ Node.js │ │ APIs │ │ (C++ layer) │ │ (DOM, fetch,│ │ + libuv + ... │ │ canvas…) │ └─────────────────────┘ └─────────────┘ |
So the same JavaScript code can run in both places, but the surrounding environment (what functions are available) is completely different.
3. What does V8 actually do? (The 4 main stages)
When you write:
|
0 1 2 3 4 5 6 7 |
const user = { name: "Aman", age: 25 }; console.log(`Hello ${user.name}`); |
V8 goes through these phases:
- Parsing → Turns your text (source code) into an Abstract Syntax Tree (AST) → Checks for syntax errors here
- Interpretation / Ignition (first execution) → V8 has a fast-but-simple interpreter called Ignition → It starts running the code immediately → Produces bytecode (intermediate representation)
- Optimization / TurboFan (when code runs many times) → If a function is called many times (hot function), V8 notices → It sends the bytecode to TurboFan (optimizing compiler) → TurboFan makes very fast machine code (native CPU instructions) → Uses information it learned from previous runs (type feedback)
- Deoptimization (rare but important) → If assumptions break (e.g. a variable that was always a number suddenly becomes a string), V8 throws away the optimized code and goes back to Ignition → This is why writing predictable types helps performance
4. Very visual example – what V8 sees vs what you write
Your code:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
function add(a, b) { return a + b; } for (let i = 0; i < 1000000; i++) { add(i, i * 2); } |
What V8 roughly does behind the scenes:
- First 10–100 calls → runs in Ignition (slow but fast startup)
- Notices: “a and b are always numbers!”
- Creates optimized version assuming numbers only
- Generates very fast machine code like:
|
0 1 2 3 4 5 6 7 8 |
mov rax, rdi ; a → register add rax, rsi ; rax = a + b ret |
- If later someone calls add(5, “hello”) → deoptimization → back to slow path
This is why in serious Node.js code people often say:
“Avoid mixing types in hot paths”
5. Real examples of V8 behavior you will meet
Example 1: Hidden classes & property order
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Fast function User(name, age) { this.name = name; this.age = age; } const u1 = new User("Aman", 25); const u2 = new User("Priya", 30); // V8 creates one "hidden class" for all User objects // Slower const u3 = {}; u3.age = 40; // different order u3.name = "Rahul"; // V8 needs to create new hidden class → slower lookups |
Lesson: Create objects in consistent order (especially in hot loops)
Example 2: Monomorphism vs Polymorphism
Fast (monomorphic):
|
0 1 2 3 4 5 6 7 8 9 10 11 |
function print(x) { console.log(x.name); } print({ name: "Aman" }); print({ name: "Sara" }); // same "shape" |
Slow (polymorphic):
|
0 1 2 3 4 5 6 7 8 |
print({ name: "Aman" }); print({ name: "Sara", age: 30 }); // different shape print(123); // completely different |
V8 loves predictable shapes in hot code.
Example 3: arguments object hurts optimization
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
function bad() { console.log(arguments[0]); // prevents many optimizations } function better(...args) { console.log(args[0]); // rest parameters are much better } |
6. How Node.js uses V8 in practice (2025–2026 view)
| Part | Who provides it | What it gives you |
|---|---|---|
| JavaScript execution | V8 | Parsing, compiling, running, optimizing JS |
| Event loop | libuv (C library) | Timers, I/O polling, async callbacks |
| File system | libuv | fs.readFile, fs.writeFile… |
| HTTP / HTTPS | Node.js C++ + llhttp | http.createServer, fetch (since 18+) |
| Streams / Buffers | Node.js C++ | Readable, Writable, Buffer class |
| require / import | Node.js | Module loading system |
| process, global | Node.js | process.env, process.argv, globalThis |
So when someone says “Node.js is single-threaded”, they mean V8 runs your JavaScript in a single thread. But libuv uses multiple threads behind the scenes for file system, DNS, crypto…
7. Quick checklist – how to write code that V8 loves
- Use const / let — never var
- Avoid arguments object in performance-sensitive code
- Keep object shapes consistent in hot loops
- Avoid delete on objects that are optimized
- Prefer for…of or .forEach over for (let i=0; …) when order doesn’t matter
- Use rest parameters (…args) instead of arguments
- Avoid try/catch inside very hot loops (can prevent optimization)
- Use modern tools (tsx, esbuild, bun) to get fast startup + V8 benefits
8. Summary – One-liners to remember
- V8 = JavaScript execution engine (same in Chrome & Node.js)
- Node.js = V8 + event loop + file system + network + modules + …
- V8 is extremely good at optimizing predictable code
- V8 is not good at heavy computation (use workers or Rust/WASM for that)
- Most “Node.js is slow” complaints actually come from bad code patterns — not V8 itself
Would you like to go deeper into any part?
Some popular follow-ups:
- How to see V8 optimization status in real code
- What –trace-opt and –trace-deopt flags show
- Writing V8-friendly code in real APIs
- How worker threads escape the single V8 thread
- Comparing V8 vs Bun vs Deno engines
- Real performance trap examples from production
Just tell me which direction you want — I’ll continue with concrete examples and code. 😄
