Unlocking V8: How Fast and Slow Properties Boost JavaScript Performance
This article dives into V8's internal optimizations for object properties, covering fast and slow properties, in‑object storage, hidden classes, array holes, fast/slow arrays, and inline caches, and offers practical advice for writing high‑performance JavaScript code.
V8 Engine Property Optimization Deep Dive
Continuing the previous sharing, this article explores detailed optimizations in the V8 engine related to object handling, fast and slow properties, in‑object properties, hidden classes, array holes, fast/slow arrays, and inline caches, providing practical insights for JavaScript developers.
Introduction
Objects are central in JavaScript. From a low‑level perspective we examine how V8 optimizes object property storage and access, and what guidance can be drawn for everyday coding.
Object Property Optimizations
Fast properties in V8
JavaScript objects behave like dictionaries, but V8 does not store them as pure hash tables. Instead it uses two linear structures—one for indexed (array‑like) properties and one for named properties—to achieve better cache locality.
function testV8() {
this[1] = 'test-1';
this["B"] = 'foo-B';
this[50] = 'test-50';
this[8] = 'test-8';
this[3] = 'test-3';
this[5] = 'test-5';
this["4"] = 'test-4';
this["A"] = 'foo-A';
this["C"] = 'foo-C';
this[4.5] = "foo-4.5";
}
const testObj = new testV8();
for (let key in testObj) {
console.log(`${key}:${testObj[key]}`);
}The console prints keys in a consistent order that does not match the definition order. V8 stores two linear structures— elements for indexed properties and properties for named ones.
When both indexed and named properties exist, indexed properties are read first. The following snapshot demonstrates this behavior.
In‑Object Properties
If an object has fewer than ten properties, V8 stores the property keys directly inside the object (“in‑object properties”), eliminating an extra indirection.
function testV8(properties, elements) {
// add indexed properties
for (let i = 0; i < elements; i++) {
this[i] = `element${i}`;
}
// add named properties
for (let i = 0; i < properties; i++) {
const prop = `property${i}`;
this[prop] = prop;
}
}
const testobj = new testV8(15, 10);
for (let key in testobj) {
console.log(`${key}:${testobj[key]}`);
}Fast vs. Slow Properties
Fast properties are stored in linear arrays for O(1) access but are costly to delete. When an object accumulates many properties, V8 switches to “slow properties”, which use a hash‑table‑like dictionary.
const testobj = new testV8(10, 10);
%DebugPrint(testobj);The transition point is roughly when the cost of a hash computation plus a linear scan becomes cheaper than scanning a large linear array (M > 2n). Frequent insertions or deletions also trigger a downgrade to slow properties.
Hidden Classes
Each object has an associated hidden class that records the layout of its properties. Adding or removing a property creates a new hidden class linked via a back‑pointer, forming a transition tree. Identical property addition sequences share the same hidden class.
const testobj1 = new testV8(2, 3);
const testobj2 = new testV8(2, 3);
testobj2.new = "new";If a hidden class already contains a transition for the same property change, V8 reuses the existing class instead of creating a new one.
Array Holes
V8 distinguishes packed arrays from “holey” arrays. When an element is deleted, a hole marker is set, and subsequent lookups may fall back to the prototype chain.
const a = [1, 2, 3];
delete a[1];
a.__proto__ = {1: 2};
console.log(a[1]); // 2Fast vs. Slow Arrays
Fast arrays use a FixedArray with contiguous memory; slow arrays fall back to a hash table when they become sparse or exceed certain size thresholds.
const a = [];
a[9999] = "9999";V8 expands the underlying storage using the formula new_capacity = old_capacity + (old_capacity >> 1) + 16.
static const uint32_t kMinAddedElementsCapacity = 16;
static uint32_t NewElementsCapacity(uint32_t old_capacity) {
return old_capacity + (old_capacity >> 1) + kMinAddedElementsCapacity;
}Inline Caches (IC)
Inline caches cache the hidden class and property offset after the first lookup, turning repeated property accesses into fast monomorphic or polymorphic loads.
function getXProps(o) {
return o.x;
}
const o1 = {x:1, y:2, z:3};
const o2 = {x:3, y:100, m:10, n:-3};
for (let i = 0; i < 10000; i++) {
getXProps(o1);
getXProps(o2);
}The feedback vector stores slots with state (mono, poly, mega) and the corresponding hidden class map and offset.
Practical Recommendations
Prefer monomorphic call sites by keeping object shapes consistent.
Avoid frequent addition or deletion of properties to prevent hidden‑class churn.
Maintain uniform property order and count across similar objects.
Do not allocate very large sparse arrays without initializing them.
Focus on real performance bottlenecks rather than micro‑optimizing V8 internals.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
