Deep dive into JavaScript engines: how V8 parses, compiles, and optimizes your code. Learn about AST, JIT compilation, hidden classes, and inline caching with examples.
Introduction
When you write console.log("Hello, World!"), something remarkable happens. Your text — a string of characters — is transformed into actual instructions that run on your computer's CPU. The component responsible for this magic is the JavaScript Engine.
Understanding the JavaScript engine helps you:
- Write faster code by avoiding patterns that cause deoptimization
- Debug performance issues more effectively
- Understand why some JavaScript "tricks" work the way they do
- Speak confidently in technical interviews about JavaScript internals
Think of the JavaScript engine as a translation machine — it takes human-readable code and turns it into machine instructions your computer can actually execute.
Real World Analogy: The Language Interpreter
Imagine you wrote a recipe in English, but your kitchen robot only understands machine code (binary instructions). You need a translator.
┌──────────────────────────────────────────────────────────────────┐
│ THE INTERPRETER ANALOGY │
├──────────────────────────────────────────────────────────────────┤
│ │
│ YOU (Developer) KITCHEN ROBOT (CPU) │
│ ────────────── ──────────────────── │
│ Write recipe in English → Robot only understands binary │
│ │
│ TRANSLATOR (JS Engine): │
│ 1. Read your recipe (Parse) │
│ 2. Understand the structure (AST) │
│ 3. Create simplified instructions (Bytecode) │
│ 4. Execute the instructions (Interpret) │
│ 5. Optimize repeated steps (JIT Compile to machine code) │
│ │
│ First time making pasta: Follow recipe step by step │
│ After making it 100 times: You have it memorized (optimized!) │
│ │
└──────────────────────────────────────────────────────────────────┘
The JavaScript Engine Pipeline
Every modern JavaScript engine processes code through these stages:
┌─────────────────────────────────────────────────────────────────┐
│ JAVASCRIPT ENGINE PIPELINE (V8) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Source Code (.js file) │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ STEP 1: PARSER │ │
│ │ Reads source code character by character │ │
│ │ Checks for syntax errors │ │
│ │ Creates tokens: keywords, identifiers, operators │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ STEP 2: ABSTRACT SYNTAX TREE (AST) │ │
│ │ Tree structure representing code semantics │ │
│ │ Each node = language construct (function, var...) │ │
│ │ Tools like ESLint use AST for static analysis │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ STEP 3: IGNITION (Interpreter) │ │
│ │ Converts AST to bytecode (platform-independent) │ │
│ │ Starts executing immediately (fast startup!) │ │
│ │ Collects "profiling" data while running │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ │ │
│ (hot code detected?) │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ STEP 4: TURBOFAN (JIT Compiler) │ │
│ │ Compiles "hot" code to optimized machine code │ │
│ │ Uses profiling data for assumptions (e.g. types) │ │
│ │ Machine code runs at near-native speed! │ │
│ │ If assumptions wrong → deoptimize & fall back │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Result: Optimized machine code runs on CPU │
│ │
└─────────────────────────────────────────────────────────────────┘
Stage 1: Parsing — Reading Your Code
The parser reads your source code as a string and converts it into structured data.
// Your source code (just a string to the engine):
// "function add(a, b) { return a + b; }"
//
// The Lexer (tokenizer) breaks it into tokens:
// ┌─────────────┬─────────────┐
// │ Token Type │ Value │
// ├─────────────┼─────────────┤
// │ Keyword │ function │
// │ Identifier │ add │
// │ Punctuator │ ( │
// │ Identifier │ a │
// │ Punctuator │ , │
// │ Identifier │ b │
// │ Punctuator │ ) │
// │ Punctuator │ { │
// │ Keyword │ return │
// │ Identifier │ a │
// │ Punctuator │ + │
// │ Identifier │ b │
// │ Punctuator │ ; │
// │ Punctuator │ } │
// └─────────────┴─────────────┘
function add(a, b) {
return a + b;
}
console.log(add(3, 4));
Syntax Errors Caught During Parsing
// The parser catches these BEFORE any code runs:
// SyntaxError: Unexpected token '='
// if (x = = 5) {} ← double equals with space
// SyntaxError: Missing closing bracket
// function broken() {
// if (true) {
// console.log("forgot to close");
//
// }
// The engine stops immediately when a syntax error is found
// No code in the file runs if there's a parse error
try {
eval("function broken( {"); // Force a parse error
} catch (e) {
console.log("Caught syntax error:", e.constructor.name);
console.log("Message:", e.message);
}
Caught syntax error: SyntaxError
Message: Unexpected token '{'
Stage 2: Abstract Syntax Tree (AST)
After tokenizing, the parser builds an Abstract Syntax Tree — a tree structure representing the semantic meaning of your code.
// This simple code:
const x = 5 + 3;
// Becomes an AST like this:
// ┌─────────────────────────────────────────────────────┐
// │ VariableDeclaration (const) │
// │ └── VariableDeclarator │
// │ ├── Identifier: "x" │
// │ └── BinaryExpression (+) │
// │ ├── NumericLiteral: 5 │
// │ └── NumericLiteral: 3 │
// └─────────────────────────────────────────────────────┘
// You can view any code's AST at: https://astexplorer.net/
// Tools that use AST:
const astTools = [
"ESLint (linting) — analyzes AST for code quality",
"Babel (transpilation) — transforms AST to older JS",
"Prettier (formatting) — reformats AST nodes",
"TypeScript — type checks using AST",
"Webpack — tree-shaking via AST analysis"
];
astTools.forEach(tool => console.log("→ " + tool));
→ ESLint (linting) — analyzes AST for code quality
→ Babel (transpilation) — transforms AST to older JS
→ Prettier (formatting) — reformats AST nodes
→ TypeScript — type checks using AST
→ Webpack — tree-shaking via AST analysis
Stage 3: Bytecode (Ignition Interpreter)
V8's Ignition interpreter converts the AST into bytecode — a compact, platform-independent intermediate format.
┌───────────────────────────────────────────────────────────────┐
│ BYTECODE vs MACHINE CODE │
├───────────────────────────────────────────────────────────────┤
│ │
│ Source Code: let result = add(3, 4); │
│ │
│ Bytecode Machine Code (x86-64) │
│ (platform- (platform-specific) │
│ independent) │
│ ────────── ────────────────── │
│ LdaGlobal 48 B8 [addr] │
│ Star r0 48 89 45 F8 │
│ LdaSmi 3 B8 03 00 00 00 │
│ Star r1 48 89 45 F0 │
│ LdaSmi 4 B8 04 00 00 00 │
│ CallRuntime E8 [offset] │
│ │
│ Bytecode is SMALLER and starts running faster. │
│ Machine code is FASTER once compiled. │
│ │
└───────────────────────────────────────────────────────────────┘
// Why start with bytecode?
// Fast startup: No need to compile everything upfront
// Less memory: Bytecode is more compact than machine code
// Portability: Same bytecode on any CPU architecture
function greet(name) {
return "Hello, " + name + "!";
}
// On first run: Ignition interprets this from bytecode
// It's a bit slower but starts IMMEDIATELY
console.log(greet("World")); // "Hello, World!"
console.log(greet("JavaScript")); // "Hello, JavaScript!"
Hello, World!
Hello, JavaScript!
Stage 4: JIT Compilation (TurboFan)
JIT (Just-In-Time) Compilation is V8's secret weapon for performance. It monitors which functions are called frequently ("hot code") and compiles them to highly optimized machine code.
┌───────────────────────────────────────────────────────────────┐
│ JIT COMPILATION PROCESS │
├───────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: Ignition runs bytecode + collects profiling data │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Called add(3, 4) → a=number, b=number → result=number│ │
│ │ Called add(10, 20) → a=number, b=number → number │ │
│ │ Called add(1, 5) → a=number, b=number → number │ │
│ │ │ │
│ │ "This function always receives numbers!" (HOT!) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ STEP 2: TurboFan compiles with type assumptions │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Generates: fast machine code assuming number inputs │ │
│ │ Result: runs at near C-speed! 🚀 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ STEP 3: Type assumption violated? → DEOPTIMIZE! │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Called add("hello", 5) → type changed! │ │
│ │ TurboFan throws away optimized code │ │
│ │ Falls back to Ignition bytecode │ │
│ │ May re-optimize with new type information │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────┘
// MONOMORPHIC function (best for JIT)
// Always called with the same types → JIT optimizes aggressively
function addNumbers(a, b) {
return a + b;
}
console.time("monomorphic");
for (let i = 0; i < 1000000; i++) {
addNumbers(i, i + 1); // Always numbers → JIT loves this!
}
console.timeEnd("monomorphic");
// POLYMORPHIC function (harder for JIT to optimize)
function addAny(a, b) {
return a + b;
}
console.time("polymorphic");
for (let i = 0; i < 1000000; i++) {
// Alternating between strings and numbers → JIT struggles
if (i % 2 === 0) {
addAny(i, i + 1); // numbers
} else {
addAny("a", "b"); // strings
}
}
console.timeEnd("polymorphic");
// Monomorphic is typically significantly faster!
monomorphic: ~5ms
polymorphic: ~25ms
Writing JIT-Friendly JavaScript
Because JIT relies on type consistency, you can write faster code by keeping types stable:
// ✅ GOOD: Consistent types in objects (V8 creates hidden classes)
function createPoint(x, y) {
return { x: x, y: y }; // Always same shape!
}
const p1 = createPoint(1, 2);
const p2 = createPoint(3, 4);
const p3 = createPoint(5, 6);
// V8 reuses the same "hidden class" for all — very fast!
// ❌ BAD: Adding properties after creation (creates new hidden classes)
const point1 = {};
point1.x = 1; // hidden class A
point1.y = 2; // hidden class B (new class created!)
// V8 can't optimize this as well
// ✅ GOOD: Type-consistent arrays
const numbers = [1, 2, 3, 4, 5]; // All numbers → JIT-optimized
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum);
// ❌ BAD: Mixed-type arrays (polymorphic)
const mixed = [1, "two", 3, true, 5]; // Mixed types → harder to optimize
Hidden Classes: V8's Object Optimization Secret
V8 uses hidden classes (similar to C++ structs) to make property access as fast as compiled languages:
// V8 internally creates hidden classes based on object shape
function Person(name, age) {
this.name = name; // hidden class C0 → C1 (added 'name')
this.age = age; // hidden class C1 → C2 (added 'age')
}
const p1 = new Person("Rahul", 25);
const p2 = new Person("Priya", 22);
const p3 = new Person("Amit", 28);
// All share the SAME hidden class C2 → fast property access!
// ⚠️ WARNING: Adding properties in different orders breaks this!
function BadPerson(name, age) {}
const bp1 = new BadPerson();
bp1.name = "Rahul"; // C0 → C1
bp1.age = 25; // C1 → C2
const bp2 = new BadPerson();
bp2.age = 22; // C0 → C3 (DIFFERENT order!)
bp2.name = "Priya"; // C3 → C4
// bp1 and bp2 have different hidden classes → SLOWER!
console.log("Always initialize properties in the same order!");
console.log("Use constructors or object literals for best performance");
Always initialize properties in the same order!
Use constructors or object literals for best performance
Inline Caching: Remembering What Worked
Inline caching (IC) is another JIT optimization where V8 remembers the results of previous property lookups:
// V8 caches the shape/offset of frequently-accessed properties
const users = [
{ name: "Rahul", age: 25 },
{ name: "Priya", age: 22 },
{ name: "Amit", age: 28 },
];
// After accessing .name a few times, V8 inlines the lookup:
// "I know that for objects shaped like this, 'name' is at offset X"
// No more lookup needed → direct memory access!
function printNames(arr) {
for (const user of arr) {
console.log(user.name); // Inline cache kicks in after 1-2 iterations
}
}
printNames(users);
Garbage Collection: Automatic Memory Management
V8 uses a Mark-and-Sweep garbage collector to automatically free memory that's no longer needed:
┌───────────────────────────────────────────────────────────────┐
│ GARBAGE COLLECTION: MARK AND SWEEP │
├───────────────────────────────────────────────────────────────┤
│ │
│ MARK PHASE: │
│ 1. Start from "roots" (global vars, call stack) │
│ 2. Traverse all reachable objects │
│ 3. Mark each reachable object as "alive" │
│ │
│ Global → obj1 ✓ → obj2 ✓ → obj3 ✓ │
│ → obj4 ✓ │
│ obj5 (unreachable) ✗ │
│ obj6 (unreachable) ✗ │
│ │
│ SWEEP PHASE: │
│ 4. Scan heap for unmarked objects │
│ 5. Free their memory │
│ │
│ COMPACT PHASE (optional): │
│ 6. Move surviving objects together │
│ 7. Reduces memory fragmentation │
│ │
└───────────────────────────────────────────────────────────────┘
// Memory is automatically freed when objects are no longer reachable
function createTemporaryData() {
const largeArray = new Array(1000000).fill(0); // ~8MB allocated
const result = largeArray.reduce((sum, n) => sum + n, 0);
return result;
// largeArray goes out of scope here → eligible for GC
}
console.log(createTemporaryData()); // 0
// After this function returns, largeArray (8MB) becomes unreachable
// GC will collect it during the next cycle
// MEMORY LEAK: Keeping unnecessary references
let cache = {};
function leakyFunction(id, data) {
cache[id] = data; // data stays forever — even if no longer needed!
}
// FIX: Use WeakMap so GC can collect when keys are gone
const safeCache = new WeakMap();
function safeFunction(keyObj, data) {
safeCache.set(keyObj, data); // GC can collect when keyObj is gone
}
console.log("GC handles memory automatically — but help it by clearing references!");
0
GC handles memory automatically — but help it by clearing references!
V8 Generations: Young and Old Space
V8 uses a generational garbage collection strategy:
┌───────────────────────────────────────────────────────────────┐
│ V8 GENERATIONAL GARBAGE COLLECTION │
├───────────────────────────────────────────────────────────────┤
│ │
│ NEW SPACE (Young Generation — ~1-8 MB) │
│ ────────────────────────────────────── │
│ • New objects allocated here │
│ • GC runs FREQUENTLY (every few ms) │
│ • Short-lived objects collected quickly (Scavenge) │
│ • Surviving objects promoted to Old Space │
│ │
│ OLD SPACE (Old Generation — larger) │
│ ────────────────────────────────── │
│ • Long-lived objects live here │
│ • GC runs INFREQUENTLY (less often) │
│ • Uses Mark-Sweep-Compact algorithm │
│ • Major GC pauses can affect performance │
│ │
│ TIP: Creating many short-lived objects is fine! │
│ GC in New Space is very efficient. │
│ │
│ AVOID: Objects that become long-lived unexpectedly │
│ (closures holding large data, global caches that grow) │
│ │
└───────────────────────────────────────────────────────────────┘
Engine Comparison: V8 vs SpiderMonkey vs JavaScriptCore
┌──────────────────────────────────────────────────────────────────┐
│ ENGINE COMPARISON TABLE │
├──────────────────────┬──────────────────┬────────────────────────┤
│ Feature │ V8 (Chrome/Node) │ SpiderMonkey (Firefox) │
├──────────────────────┼──────────────────┼────────────────────────┤
│ Interpreter │ Ignition │ WarpMonkey │
│ JIT Compiler │ TurboFan │ IonMonkey │
│ GC Algorithm │ Orinoco │ Generational GC │
│ WebAssembly │ Liftoff + TF │ Cranelift + Ion │
│ Created by │ Google │ Mozilla │
│ Open Source │ Yes │ Yes │
│ Used in │ Chrome, Node, │ Firefox, SpiderNode │
│ │ Edge, Deno │ │
│ Performance focus │ Web + Server │ Browser + Privacy │
└──────────────────────┴──────────────────┴────────────────────────┘
// ✅ TIP 1: Use typed arrays for numerical data (engine optimizes these)
const typedArr = new Float64Array(1000);
for (let i = 0; i < 1000; i++) typedArr[i] = i * 1.5;
console.log("Typed array sum:", typedArr.reduce((sum, n) => sum + n, 0));
// ✅ TIP 2: Avoid delete — it breaks hidden classes
const obj = { a: 1, b: 2, c: 3 };
// BAD: delete obj.b; // Destroys hidden class optimization!
// GOOD: Set to null instead if you need to "empty" a property
obj.b = null;
console.log("b is now:", obj.b);
// ✅ TIP 3: Inline small functions (TurboFan does this automatically)
// But help by keeping hot functions small and focused
// ✅ TIP 4: Avoid with statement — makes optimization impossible!
// const result = with(obj) { a + b }; // ← V8 CANNOT optimize this!
// ✅ TIP 5: Use strict mode — helps V8 make better assumptions
"use strict";
// ✅ TIP 6: Keep array types consistent
const numArray = []; // V8 will use SMI (Small Integer) representation
for (let i = 0; i < 100; i++) numArray.push(i); // All integers — fast!
console.log("Arrays length:", numArray.length);
Typed array sum: 74925
b is now: null
Arrays length: 100
How V8 Handles Specific JavaScript Features
// Closures in V8
function makeAdder(x) {
// V8 creates a "context" object to hold 'x'
// The returned function has a reference to this context
return function(y) {
return x + y; // 'x' is found in the closure context
};
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
console.log(add5(3)); // 8
console.log(add10(3)); // 13
// Each call to makeAdder creates a new context object in the heap
// The returned function holds a reference → GC won't collect x
// Prototype chain lookup in V8
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return this.name + " speaks";
};
const dog = new Animal("Rex");
// V8 looks up: dog → Animal.prototype → Object.prototype → null
// After the first lookup, inline cache remembers the location!
console.log(dog.speak()); // Rex speaks
Common Mistakes That Break JIT Optimization
1. Changing Object Shape After Creation
// ❌ BAD: Adding properties dynamically
function processUser(user) {
console.log(user.name, user.age); // V8 expects {name, age}
}
const user = { name: "Rahul" };
user.age = 25; // Added later → different hidden class!
user.email = "test@test.com"; // Another dynamic addition
// ✅ GOOD: Define all properties upfront
const goodUser = {
name: "Rahul",
age: 25,
email: "test@test.com" // All defined together → stable hidden class
};
processUser(goodUser);
2. Using Arguments Object in Hot Functions
// ❌ BAD: arguments object prevents optimization
function badSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i]; // 'arguments' is slow in hot functions!
}
return total;
}
// ✅ GOOD: Use rest parameters instead
function goodSum(...args) {
return args.reduce((total, n) => total + n, 0);
}
console.log(goodSum(1, 2, 3, 4, 5)); // 15
3. try/catch in Hot Loops
// ❌ BAD: try/catch inside tight loop (prevents optimization)
function processItems(items) {
const results = [];
for (let i = 0; i < items.length; i++) {
try {
results.push(items[i].value * 2); // try/catch in loop = slow!
} catch (e) {
results.push(0);
}
}
return results;
}
// ✅ GOOD: Move try/catch outside the hot path
function safeDouble(item) {
try {
return item.value * 2;
} catch (e) {
return 0;
}
}
function processItemsFast(items) {
return items.map(safeDouble); // try/catch isolated from loop
}
const items = [{value: 1}, {value: 2}, null, {value: 4}];
console.log(processItemsFast(items));
Key Takeaways
| Concept | Description | Performance Impact |
|---|
| Parsing | Source code → tokens → AST | One-time cost per script |
| Ignition | AST → bytecode, runs immediately | Fast startup, modest speed |
| TurboFan | Bytecode → optimized machine code | Near-native speed for hot code |
| Hidden Classes | Objects with same shape share class | Keeps property access fast |
| Inline Caching | Caches property lookup results | Eliminates repeated lookups |
| JIT | Compile hot code at runtime | 10-100x faster than interpreted |
| GC (Young) | Quick collection of short-lived objects | Frequent but fast |
| GC (Old) | Collect long-lived objects | Infrequent but can pause |
| Deoptimization | Revert optimized code on type change | Avoid by keeping types stable |
Interview Questions
Q1: What is a JavaScript engine?
Answer: A JavaScript engine is a program that parses, interprets, and compiles JavaScript source code into machine code that the CPU can execute. Examples include V8 (Chrome, Node.js), SpiderMonkey (Firefox), and JavaScriptCore (Safari). They provide the core language execution environment.
Q2: What is the difference between V8, SpiderMonkey, and JavaScriptCore?
Answer: All three are JavaScript engines implementing the ECMAScript specification, but with different internal implementations. V8 (Google) is used in Chrome, Node.js, and Edge — known for high performance. SpiderMonkey (Mozilla) powers Firefox and focuses on standards compliance and security. JavaScriptCore (Apple) powers Safari and Bun, optimized for Apple hardware.
Q3: What is JIT compilation in JavaScript?
Answer: Just-In-Time (JIT) compilation is a technique where frequently-executed ("hot") JavaScript code is compiled to optimized machine code at runtime — just before it's needed. V8's TurboFan compiles hot functions to machine code after gathering profiling data from Ignition (the interpreter), resulting in near-native execution speed.
Q4: What is an Abstract Syntax Tree (AST)?
Answer: An AST is a tree data structure representing the syntactic structure of source code. Each node represents a language construct (variable declaration, function call, expression, etc.). The parser generates the AST from tokens. Tools like ESLint, Babel, TypeScript, and Prettier all use AST to analyze and transform code.
Q5: What are hidden classes in V8?
Answer: Hidden classes are V8's internal optimization for JavaScript objects. When objects are created with the same properties in the same order, V8 assigns them the same "hidden class" — a C++-like struct definition that enables fast property access. Adding properties dynamically in different orders creates new hidden classes and prevents this optimization.
Q6: What is deoptimization in JavaScript?
Answer: Deoptimization occurs when V8's JIT compiler made type assumptions to generate fast machine code, but those assumptions are violated at runtime. V8 throws away the optimized code and falls back to interpreted bytecode. Common causes include: changing an object's property types, accessing array holes, or violating function signatures.
Q7: What is garbage collection in JavaScript?
Answer: Garbage collection is automatic memory management — V8 tracks which objects are still reachable from "roots" (global variables, call stack) and frees memory for unreachable objects. V8 uses a generational approach: most objects die young (collected in New Space via Scavenge), while long-lived objects move to Old Space (collected via Mark-Sweep-Compact).
Q8: How does V8's Ignition interpreter work?
Answer: Ignition is V8's baseline interpreter. It converts the AST to compact bytecode and executes it immediately. While executing, Ignition collects "profiling feedback" — tracking which functions are called often and what types they receive. This profiling data is used by TurboFan to generate highly optimized machine code for hot functions.
Q9: What is inline caching?
Answer: Inline caching (IC) is an optimization where V8 caches the result of property lookups after the first access. When code accesses obj.name repeatedly on objects with the same hidden class, V8 inlines the exact memory offset — turning a lookup into a direct memory read, similar to accessing a C struct field.
Q10: How can you write JavaScript that's easier for the engine to optimize?
Answer: Key practices: (1) Keep object shapes consistent — define all properties in constructors, (2) Maintain type consistency — don't mix types in arrays or function parameters, (3) Avoid delete — set properties to null instead, (4) Use "use strict" mode, (5) Prefer rest parameters over arguments object, (6) Keep hot functions small, (7) Avoid with statement and eval(), (8) Initialize object properties in the same order.
Summary
The JavaScript engine is a complex pipeline that transforms your human-readable code into blazing-fast machine instructions:
Your Code → Parser → AST → Ignition Bytecode → [JIT] → Optimized Machine Code
↑ ↓
Profiling data Near-native speed!
collected here
Key takeaways:
- Parsing happens first — syntax errors stop everything
- Ignition gets code running fast (bytecode, no compilation wait)
- TurboFan makes hot code fast (JIT compilation with type assumptions)
- Hidden classes make object property access as fast as C
- Inline caching eliminates repeated property lookups
- Generational GC handles memory automatically — mostly without pausing
- Writing JIT-friendly code can make your app 10x faster
Understanding the engine doesn't just help with performance — it also explains many "quirky" JavaScript behaviors and makes you a more thoughtful developer.
What's Next?
In the next lesson, we'll explore JavaScript Use Cases — the incredible variety of things you can actually build with JavaScript, from games to IoT devices to AI applications.