Docs

24.2-Hidden-Classes-Shapes

22.2 Hidden Classes and Inline Caching

Understanding hidden classes and inline caching is crucial for writing high-performance JavaScript. These V8 optimizations are key to fast property access.


Table of Contents

  1. β€’Object Representation in V8
  2. β€’Hidden Classes (Shapes/Maps)
  3. β€’Hidden Class Transitions
  4. β€’Inline Caching (IC)
  5. β€’IC States: Monomorphic, Polymorphic, Megamorphic
  6. β€’Optimization Patterns
  7. β€’Anti-Patterns to Avoid

Object Representation in V8

JavaScript objects are dynamicβ€”properties can be added or removed at any time. But V8 needs to make property access fast. The solution: Hidden Classes.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    OBJECT REPRESENTATION                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  NAIVE APPROACH (Slow):                                             β”‚
β”‚  ─────────────────────                                             β”‚
β”‚  JavaScript Object β†’ Dictionary/Hash Map                            β”‚
β”‚                                                                     β”‚
β”‚  { x: 10, y: 20 }                                                   β”‚
β”‚       ↓                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                               β”‚
β”‚  β”‚  Hash Map       β”‚  ← Each property lookup requires:             β”‚
β”‚  β”‚  "x" β†’ 10       β”‚    1. Compute hash of "x"                     β”‚
β”‚  β”‚  "y" β†’ 20       β”‚    2. Find bucket                             β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    3. Handle collisions                        β”‚
β”‚                         4. Return value                             β”‚
β”‚                                                                     β”‚
β”‚  V8 APPROACH (Fast):                                                β”‚
β”‚  ──────────────────                                                β”‚
β”‚  JavaScript Object β†’ Hidden Class + Fixed-Layout Storage            β”‚
β”‚                                                                     β”‚
β”‚  { x: 10, y: 20 }                                                   β”‚
β”‚       ↓                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚
β”‚  β”‚ Hidden Class │────→│  Object Storage    β”‚                       β”‚
β”‚  β”‚ x @ offset 0 β”‚     β”‚  [0]: 10  (x)      β”‚                       β”‚
β”‚  β”‚ y @ offset 8 β”‚     β”‚  [8]: 20  (y)      β”‚                       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚
β”‚                                                                     β”‚
β”‚  Property access: Just read from known offset!                      β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Hidden Classes (Shapes/Maps)

Hidden classes (also called "shapes" or "maps") describe the structure of objects.

What Hidden Classes Store

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    HIDDEN CLASS STRUCTURE                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  Hidden Class for { x: number, y: number }                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  Property Name  β”‚  Offset  β”‚  Type Hint   β”‚  Attributes    β”‚    β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”‚
β”‚  β”‚  "x"            β”‚  0       β”‚  SMI/Number  β”‚  Writable      β”‚    β”‚
β”‚  β”‚  "y"            β”‚  8       β”‚  SMI/Number  β”‚  Writable      β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                                     β”‚
β”‚  Additional Metadata:                                               β”‚
β”‚  β€’ Prototype reference                                              β”‚
β”‚  β€’ Constructor reference                                            β”‚
β”‚  β€’ Transition table (for adding properties)                         β”‚
β”‚  β€’ Number of properties                                             β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Objects Sharing Hidden Classes

// These objects share the SAME hidden class
const point1 = { x: 10, y: 20 };
const point2 = { x: 30, y: 40 };
const point3 = { x: 50, y: 60 };
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SHARED HIDDEN CLASS                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                   β”‚
β”‚              β”‚  Hidden Class   β”‚                                   β”‚
β”‚              β”‚  x @ offset 0   β”‚                                   β”‚
β”‚              β”‚  y @ offset 8   β”‚                                   β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                   β”‚
β”‚                       β”‚                                             β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚
β”‚         ↓             ↓             ↓                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”‚
β”‚  β”‚  point1   β”‚ β”‚  point2   β”‚ β”‚  point3   β”‚                        β”‚
β”‚  β”‚  [0]: 10  β”‚ β”‚  [0]: 30  β”‚ β”‚  [0]: 50  β”‚                        β”‚
β”‚  β”‚  [8]: 20  β”‚ β”‚  [8]: 40  β”‚ β”‚  [8]: 60  β”‚                        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β”‚
β”‚                                                                     β”‚
β”‚  Benefits:                                                          β”‚
β”‚  β€’ Memory efficient (one hidden class for many objects)            β”‚
β”‚  β€’ Enables inline caching (same access pattern)                    β”‚
β”‚  β€’ Fast property access (known offsets)                            β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Hidden Class Transitions

When you add a property to an object, V8 transitions to a new hidden class.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    HIDDEN CLASS TRANSITIONS                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  const point = {};    // Hidden Class C0 (empty)                   β”‚
β”‚  point.x = 10;        // Transition to C1 (has x)                  β”‚
β”‚  point.y = 20;        // Transition to C2 (has x, y)               β”‚
β”‚                                                                     β”‚
β”‚  Transition Chain:                                                  β”‚
β”‚                                                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  add 'x'  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  add 'y'  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚
β”‚  β”‚   C0     β”‚ ────────→ β”‚   C1     β”‚ ────────→ β”‚   C2     β”‚       β”‚
β”‚  β”‚ (empty)  β”‚           β”‚ x @ 0    β”‚           β”‚ x @ 0    β”‚       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚ y @ 8    β”‚       β”‚
β”‚                                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚
β”‚                                                                     β”‚
β”‚  Transition Tree (for property additions):                         β”‚
β”‚                                                                     β”‚
β”‚                         C0 (empty)                                  β”‚
β”‚                        /          \                                 β”‚
β”‚                 add 'x'            add 'y'                         β”‚
β”‚                      ↓                  ↓                           β”‚
β”‚                     C1                 C3                          β”‚
β”‚                   (x @ 0)            (y @ 0)                       β”‚
β”‚                      |                  |                           β”‚
β”‚                add 'y'            add 'x'                          β”‚
β”‚                      ↓                  ↓                           β”‚
β”‚                     C2                 C4                          β”‚
β”‚                (x @ 0, y @ 8)    (y @ 0, x @ 8)                   β”‚
β”‚                                                                     β”‚
β”‚  ⚠️ C2 β‰  C4: Different hidden classes for same properties!        β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Property Order Matters!

// These create DIFFERENT hidden classes
function createPointA() {
  const p = {};
  p.x = 0; // First
  p.y = 0; // Second
  return p;
}

function createPointB() {
  const p = {};
  p.y = 0; // First (different order!)
  p.x = 0; // Second
  return p;
}

const a = createPointA(); // Hidden class: x@0, y@8
const b = createPointB(); // Hidden class: y@0, x@8

// These are incompatible for inline caching!

Inline Caching (IC)

Inline caching remembers how property access worked last time to speed up future access.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      INLINE CACHING                                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  function getX(obj) {                                               β”‚
β”‚    return obj.x;  // ← Property access site                        β”‚
β”‚  }                                                                  β”‚
β”‚                                                                     β”‚
β”‚  WITHOUT INLINE CACHING (Slow):                                     β”‚
β”‚  ───────────────────────────────                                   β”‚
β”‚  Every call:                                                        β”‚
β”‚  1. Look up "x" in obj's hidden class                              β”‚
β”‚  2. Find offset                                                     β”‚
β”‚  3. Load value from offset                                          β”‚
β”‚                                                                     β”‚
β”‚  WITH INLINE CACHING (Fast):                                        β”‚
β”‚  ────────────────────────────                                      β”‚
β”‚  First call:                                                        β”‚
β”‚  1. Look up "x" in obj's hidden class β†’ offset 0                   β”‚
β”‚  2. CACHE: "For hidden class C2, x is at offset 0"                 β”‚
β”‚  3. Load value                                                      β”‚
β”‚                                                                     β”‚
β”‚  Subsequent calls with same hidden class:                           β”‚
β”‚  1. Check: Is obj's hidden class C2? Yes!                          β”‚
β”‚  2. Load directly from offset 0  ← FAST PATH                       β”‚
β”‚                                                                     β”‚
β”‚  Generated Code Evolution:                                          β”‚
β”‚                                                                     β”‚
β”‚  // Uninitialized                                                   β”‚
β”‚  obj.x β†’ GenericPropertyLoad("x")                                  β”‚
β”‚                                                                     β”‚
β”‚  // After first call (Monomorphic)                                 β”‚
β”‚  obj.x β†’ if (obj.map === C2) LoadField(0) else Slow()             β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

IC States

Inline caches have different states based on how many different object shapes they've seen.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    IC STATES                                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  1. UNINITIALIZED                                                   β”‚
β”‚     β€’ Property access has never executed                            β”‚
β”‚     β€’ Will transition on first execution                            β”‚
β”‚                                                                     β”‚
β”‚  2. MONOMORPHIC ⭐ (Fastest)                                        β”‚
β”‚     β€’ Only seen ONE hidden class                                    β”‚
β”‚     β€’ Direct memory access                                          β”‚
β”‚     β€’ Single comparison check                                       β”‚
β”‚                                                                     β”‚
β”‚  3. POLYMORPHIC (Still fast)                                        β”‚
β”‚     β€’ Seen 2-4 different hidden classes                            β”‚
β”‚     β€’ Small lookup table                                            β”‚
β”‚     β€’ Linear search through known shapes                            β”‚
β”‚                                                                     β”‚
β”‚  4. MEGAMORPHIC ⚠️ (Slow)                                          β”‚
β”‚     β€’ Seen 5+ different hidden classes                              β”‚
β”‚     β€’ Falls back to dictionary lookup                               β”‚
β”‚     β€’ No caching benefit                                            β”‚
β”‚                                                                     β”‚
β”‚  Transition Diagram:                                                β”‚
β”‚                                                                     β”‚
β”‚  UNINITIALIZED ──(first call)──→ MONOMORPHIC                       β”‚
β”‚                                       β”‚                             β”‚
β”‚                          (new shape)  β”‚                             β”‚
β”‚                                       ↓                             β”‚
β”‚                               POLYMORPHIC                           β”‚
β”‚                                       β”‚                             β”‚
β”‚                         (5+ shapes)   β”‚                             β”‚
β”‚                                       ↓                             β”‚
β”‚                               MEGAMORPHIC                           β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

IC State Examples

function getX(obj) {
  return obj.x;
}

// Monomorphic - FAST ⭐
const points = [];
for (let i = 0; i < 1000; i++) {
  points.push({ x: i, y: i * 2 }); // All same shape
}
points.forEach((p) => getX(p)); // IC stays monomorphic

// Polymorphic - Still OK
function getX2(obj) {
  return obj.x;
}
getX2({ x: 1, y: 2 }); // Shape A
getX2({ x: 1, y: 2, z: 3 }); // Shape B
getX2({ x: 1 }); // Shape C
// IC is polymorphic (3 shapes)

// Megamorphic - SLOW ⚠️
function getX3(obj) {
  return obj.x;
}
getX3({ x: 1 });
getX3({ x: 1, a: 2 });
getX3({ x: 1, b: 2 });
getX3({ x: 1, c: 2 });
getX3({ x: 1, d: 2 });
getX3({ x: 1, e: 2 });
// IC becomes megamorphic (6+ shapes)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    IC STATE PERFORMANCE                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  Performance Comparison (relative):                                 β”‚
β”‚                                                                     β”‚
β”‚  MONOMORPHIC   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  100%               β”‚
β”‚  POLYMORPHIC   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ              60-80%             β”‚
β”‚  MEGAMORPHIC   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                          20-30%             β”‚
β”‚                                                                     β”‚
β”‚  Generated Code:                                                    β”‚
β”‚                                                                     β”‚
β”‚  MONOMORPHIC:                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚  cmp [obj + map_offset], expected_map            β”‚              β”‚
β”‚  β”‚  jne slow_path                                   β”‚              β”‚
β”‚  β”‚  mov result, [obj + 0]  ; Direct load!           β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                                                     β”‚
β”‚  POLYMORPHIC (3 shapes):                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚  mov map, [obj + map_offset]                     β”‚              β”‚
β”‚  β”‚  cmp map, map1; je load_at_0                     β”‚              β”‚
β”‚  β”‚  cmp map, map2; je load_at_8                     β”‚              β”‚
β”‚  β”‚  cmp map, map3; je load_at_16                    β”‚              β”‚
β”‚  β”‚  jmp slow_path                                   β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                                                     β”‚
β”‚  MEGAMORPHIC:                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚  call GenericPropertyLookup  ; Dictionary lookup β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Optimization Patterns

Pattern 1: Consistent Object Initialization

// βœ… GOOD: All objects have same hidden class
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

// Factory function with consistent shape
function createPoint(x, y) {
  return { x, y }; // Always same order
}

// βœ… GOOD: All properties initialized upfront
function createUser(name, age) {
  return {
    name: name,
    age: age,
    email: null, // Initialize even if null
    active: false, // Initialize even if false
    score: 0, // Initialize even if 0
  };
}

Pattern 2: Initialize All Properties in Constructor

// ❌ BAD: Properties added later
class Person {
  constructor(name) {
    this.name = name;
    // age added conditionally later
  }

  setAge(age) {
    this.age = age; // Hidden class transition!
  }
}

// βœ… GOOD: All properties in constructor
class Person {
  constructor(name, age = null) {
    this.name = name;
    this.age = age; // Always present
  }
}

Pattern 3: Avoid Dynamic Property Names

// ❌ BAD: Dynamic property names
function setProperty(obj, key, value) {
  obj[key] = value; // Each key creates new hidden class
}

const obj = {};
setProperty(obj, 'a', 1);
setProperty(obj, 'b', 2);
setProperty(obj, 'c', 3);

// βœ… GOOD: Use Map for dynamic keys
const data = new Map();
data.set('a', 1);
data.set('b', 2);
data.set('c', 3);

Pattern 4: Monomorphic Functions

// ❌ BAD: Called with different shapes
function processItem(item) {
  return item.value * 2;
}

processItem({ value: 1 });
processItem({ value: 2, extra: 3 });
processItem({ other: 0, value: 4 });

// βœ… GOOD: Consistent input shapes
function processNumber(num) {
  return num * 2;
}

processNumber(1);
processNumber(2);
processNumber(4);

Anti-Patterns to Avoid

Anti-Pattern 1: Deleting Properties

// ❌ BAD: delete causes hidden class transition
const user = { name: 'Alice', age: 25, temp: 'data' };
delete user.temp; // Transitions to "dictionary mode"!

// βœ… GOOD: Set to null/undefined instead
const user = { name: 'Alice', age: 25, temp: 'data' };
user.temp = null; // Keeps same hidden class
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    DELETE PROPERTY EFFECT                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  Before delete:                                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                            β”‚
β”‚  β”‚ Hidden Class │────→│  Fast Object  β”‚                            β”‚
β”‚  β”‚ name @ 0     β”‚     β”‚  [0]: "Alice" β”‚                            β”‚
β”‚  β”‚ age @ 8      β”‚     β”‚  [8]: 25      β”‚                            β”‚
β”‚  β”‚ temp @ 16    β”‚     β”‚  [16]: "data" β”‚                            β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                            β”‚
β”‚                                                                     β”‚
β”‚  After delete user.temp:                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚  β”‚ Dictionary Mode  │────→│  Slow Hash Table    β”‚                  β”‚
β”‚  β”‚ (No fixed shape) β”‚     β”‚  "name" β†’ "Alice"   β”‚                  β”‚
β”‚  β”‚                  β”‚     β”‚  "age" β†’ 25         β”‚                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                                                                     β”‚
β”‚  ⚠️ Object permanently becomes slow!                               β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Anti-Pattern 2: Inconsistent Property Order

// ❌ BAD: Different property order
function createResponseA(success, data) {
  return { success, data }; // success first
}

function createResponseB(data, success) {
  return { data, success }; // data first
}

// βœ… GOOD: Consistent order everywhere
function createResponse(success, data) {
  return { success, data }; // Always same order
}

Anti-Pattern 3: Modifying Prototypes

// ❌ BAD: Modifying built-in prototypes
Array.prototype.sum = function () {
  return this.reduce((a, b) => a + b, 0);
};
// This invalidates optimized code!

// βœ… GOOD: Use helper functions
function sum(arr) {
  return arr.reduce((a, b) => a + b, 0);
}

Anti-Pattern 4: Adding Properties Outside Constructor

// ❌ BAD: Properties added after construction
class Widget {
  constructor() {
    this.id = Math.random();
  }

  init() {
    this.width = 100; // New hidden class
    this.height = 100; // Another transition
  }
}

// βœ… GOOD: All properties in constructor
class Widget {
  constructor() {
    this.id = Math.random();
    this.width = 0; // Initialize all
    this.height = 0;
  }

  init() {
    this.width = 100; // Same hidden class
    this.height = 100;
  }
}

Debugging Hidden Classes

Using Chrome DevTools

// In Chrome DevTools Console:
// 1. Take a heap snapshot
// 2. Look for objects with same constructor
// 3. Check if they have same "map" (hidden class)

// Using V8 native syntax (Node.js with --allow-natives-syntax):
// %HaveSameMap(obj1, obj2)  // Returns true if same hidden class

Using Node.js Flags

# Trace IC state changes
node --trace-ic your-script.js

# Trace hidden class transitions
node --trace-maps your-script.js

# Trace deoptimizations
node --trace-deopt your-script.js

Key Takeaways

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              HIDDEN CLASSES & IC SUMMARY                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  HIDDEN CLASSES:                                                    β”‚
β”‚  β€’ Objects with same shape share hidden classes                     β”‚
β”‚  β€’ Property order matters!                                          β”‚
β”‚  β€’ Adding/deleting properties creates transitions                   β”‚
β”‚  β€’ Initialize all properties upfront                                β”‚
β”‚                                                                     β”‚
β”‚  INLINE CACHING:                                                    β”‚
β”‚  β€’ Caches property access patterns                                  β”‚
β”‚  β€’ Monomorphic = fastest (one shape)                               β”‚
β”‚  β€’ Polymorphic = OK (2-4 shapes)                                   β”‚
β”‚  β€’ Megamorphic = slow (5+ shapes)                                  β”‚
β”‚                                                                     β”‚
β”‚  OPTIMIZATION TIPS:                                                 β”‚
β”‚  βœ… Use classes/factories for consistent shapes                    β”‚
β”‚  βœ… Initialize all properties in constructors                       β”‚
β”‚  βœ… Keep property order consistent                                  β”‚
β”‚  βœ… Use null instead of delete                                      β”‚
β”‚  βœ… Use Map for truly dynamic keys                                  β”‚
β”‚                                                                     β”‚
β”‚  AVOID:                                                             β”‚
β”‚  ❌ delete operator                                                 β”‚
β”‚  ❌ Adding properties after initialization                          β”‚
β”‚  ❌ Different property orders for same type                         β”‚
β”‚  ❌ Modifying prototypes at runtime                                 β”‚
β”‚  ❌ Too many object shapes in one function                          β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Next Steps

Continue to 22.3 Advanced Garbage Collection to understand how V8 manages memory with generational and incremental garbage collection.

.2 Hidden Classes Shapes - JavaScript Tutorial | DeepML