Chapter 6.1☕ 20 min read

Array Internals: Packed, Holey & 6 Element Kinds

Ek float daalo, poora array slow — yeh V8 ka one-way transition hai, wapas nahi jaate.

01Element Kinds — SMI, Double, Object Ka Fareq

V8 tracks the type of elements in an array to optimize memory layout and access speed. It categorizes arrays into 6 element kinds: PACKED_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, PACKED_ELEMENTS, and their HOLEY counterparts.

SMI (Small Integer): 31-bit integers stored directly in memory. Fastest possible array type — values fit in CPU registers.

DOUBLE: 64-bit floating point numbers. When you add a single float to a SMI array, the entire array is converted to DOUBLE storage.

ELEMENTS: References to objects, strings, or other values. When you add an object to a SMI or DOUBLE array, it converts to ELEMENTS storage.

// SMI Array (fastest - integers only)
const smiArr = [1, 2, 3]; // PACKED_SMI_ELEMENTS

// DOUBLE Array (float added, downgrade triggered)
smiArr.push(1.5); // Now PACKED_DOUBLE_ELEMENTS (entire array converted!)

// OBJECT Array (object added, downgrade triggered)
smiArr.push({ x: 1 }); // Now PACKED_ELEMENTS (slowest of the 3)

// The transition is PERMANENT for this array instance
// Even if you remove the float and object, it stays PACKED_ELEMENTS
V8 uses element kinds to generate optimized machine code. If TurboFan sees an array is PACKED_SMI, it can unroll math loops using CPU registers. If it becomes PACKED_DOUBLE, V8 must generate code to handle 64-bit floats. Changing kinds causes a "deoptimization" — V8 throws away the optimized code and recompiles.
02Packed vs Holey — Hole Check Ki Saza

PACKED: Contiguous memory, no empty slots. Every index has a value. V8 can do direct pointer arithmetic for access.

HOLEY: Has gaps (empty slots). Created by: missing indices, delete arr[i], or Array(n).

V8 must perform a "hole check" on every access in a HOLEY array. Accessing a hole returns undefined, but V8 has to check the prototype chain first — a slow operation.

Creating holes is one of the easiest ways to ruin array performance.

// PACKED Array (fast)
const packed = [1, 2, 3, 4]; // PACKED_SMI_ELEMENTS

// HOLEY Array (slow - has gaps)
const holey = [1, , , 4]; // HOLEY_SMI_ELEMENTS

console.log(holey[1]); // undefined
// V8 check: Is index 1 a hole? Yes - walk prototype chain - return undefined.

// The delete operator creates holes
const arr = [1, 2, 3];
delete arr[1]; // arr is now [1, , 3] (HOLEY_SMI_ELEMENTS)
// NEVER use delete on arrays! Use splice() instead.

// Array constructor creates holes
const sparse = new Array(3); // [empty x 3] (HOLEY_SMI_ELEMENTS)
sparse.push(1); // [empty x 3, 1]
03Dictionary Mode — Sabse Slowest Path

If an array becomes extremely sparse (lots of holes), V8 switches to Dictionary Mode.

Dictionary mode uses a hash table instead of a contiguous block of memory. Property access becomes O(n) hash lookup instead of O(1) pointer arithmetic.

Triggered by: very large gaps, deleting indices frequently.

Array.isArray() still returns true, but internally it behaves nothing like a fast array.

// Triggering Dictionary Mode
const arr = [];
arr[100000] = 'way out there'; // Huge gap = sparse array

// V8 decides: allocating 100,000 empty slots wastes memory
// Instead, it creates a hash table internally

// The performance difference
const fastArr = [1, 2, 3, 4, 5];
const slowArr = [];
slowArr[50000] = 1; // Dictionary mode triggered

console.time('fast');
for (let i = 0; i < fastArr.length; i++) { fastArr[i]; }
console.timeEnd('fast');

console.time('slow');
for (let i = 0; i < slowArr.length; i++) { slowArr[i]; } // Iterates 50k empty slots!
console.timeEnd('slow');
Pro tip: If you need a sparse lookup where keys are arbitrary numbers, use a Map or Object, not an Array. Arrays are optimized for contiguous, dense data. Using them as hash tables defeats V8's optimizations entirely.
04Methods Aur Holes — Map Skip Kar Deta Hai

How array methods handle holes is inconsistent and surprising.

map, filter, forEach SKIP holes entirely — the callback is never called for holes.

join and toString treat holes as empty strings.

Array.from and spread [...arr] convert holes to undefined (filling them in).

Array.from(arrayLike) creates a PACKED array — a good way to "heal" a holey array.

const holey = [1, , 3];

// map SKIPS holes
holey.map(x => x * 2); // [2, empty, 6]

// forEach SKIPS holes
holey.forEach(x => console.log(x)); // logs 1, 3 (skips the hole!)

// join treats holes as empty string
holey.join('-'); // "1--3"

// Spread and Array.from FILL holes with undefined
const packedCopy = [...holey]; // [1, undefined, 3]
const fromCopy = Array.from(holey); // [1, undefined, 3]

// Healing a holey array
const healed = Array.from(holey); // Now PACKED_ELEMENTS (contains undefined)
healed.map(x => x * 2); // [2, NaN, 6] (callback runs for undefined now!)
05length Ka Asli Matlab — Count Nahi, Highest Index + 1

Array length is the highest numeric index + 1, NOT the count of values.

Setting arr.length = 0 is a fast way to clear an array (truncates it).

Setting arr.length = 10 on a shorter array creates holes.

length is writable and configurable, but usually you shouldn't modify it manually.

const arr = [];
arr[10] = 'end';
console.log(arr.length); // 11 (10 + 1)
console.log(arr); // [empty x 10, 'end']

// Truncating an array
const data = [1, 2, 3, 4, 5];
data.length = 2;
console.log(data); // [1, 2] (rest are gone forever)

// Extending creates holes
data.length = 5;
console.log(data); // [1, 2, empty x 3]

// Fast clear trick
const cache = [1, 2, 3];
cache.length = 0; // Empties the array, but keeps the reference
Clearing arrays: Setting arr.length = 0 is the fastest way to empty an array if you need to keep the same reference (e.g., other variables point to this array). It removes all elements without allocating a new array object.

Lo kar liya — Key Points:

  • ✅ V8 categorizes arrays into 6 element kinds: PACKED_SMI, PACKED_DOUBLE, PACKED_ELEMENTS, and their HOLEY versions
  • ✅ Transitions are one-way: SMI → DOUBLE → OBJECT. Adding a float to an integer array permanently downgrades it
  • ✅ Holey arrays require V8 to perform "hole checks" on every access, degrading performance
  • ✅ Extremely sparse arrays trigger Dictionary Mode, using a hash table instead of contiguous memory (very slow)
  • ✅ Array methods like map, filter, and forEach skip holes entirely; spread and Array.from convert holes to undefined
  • ✅ Array length is the highest index + 1, not the count of elements; modifying length can truncate or create holes
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase