How Hidden Classes Optimize JavaScript in Chromium
This article explores how the Chromium web browser, specifically through its V8 JavaScript engine, optimizes property access on dynamic JavaScript objects using hidden classes, internally known as Maps. Because JavaScript is a dynamically typed language, objects can change structure at runtime, which traditionally forces engines to use slow dictionary-like lookups to find properties. You will learn how V8 overcomes this limitation by assigning virtual shapes to objects, creating transition chains, and utilizing inline caching to achieve property access speeds comparable to statically typed languages.
The Challenge of Dynamic Objects
In statically typed languages like C++, object structures are fixed at compile time. Because the layout of an object never changes, the compiler knows the exact memory offset of every property. Accessing a property is as simple as reading a specific memory address.
JavaScript is dynamically typed, allowing developers to add or delete properties on the fly. Without optimization, a JavaScript engine must treat objects like hash tables (dictionaries). Every time a property is accessed, the engine has to compute a hash of the property name to find its value in memory. This process is computationally expensive and significantly slows down execution.
What Are Hidden Classes (Maps)?
To avoid slow dictionary lookups, Chromium’s V8 engine attaches a hidden class—referred to as a "Map" in V8's source code—to every JavaScript object.
A hidden class separates an object's structure (its shape) from its data (its values). Instead of storing property names and values together in a dictionary, V8 stores the object's values in a flat, contiguous array in memory. The hidden class acts as a blueprint, storing the property names and their corresponding index (memory offset) within that value array.
When a property is accessed, V8 consults the object's hidden class, retrieves the exact memory offset, and jumps straight to the value in the array.
Transition Chains and Shared Shapes
Because JavaScript objects mutate, hidden classes must adapt dynamically. V8 handles this by creating "transition chains" as properties are added to an object:
- Initial State: When you create an empty object
(
const obj = {}), V8 assigns it an initial empty hidden class, let's call itC0. - First Property: When you add a property
(
obj.x = 10), V8 creates a new hidden class,C1, which inherits fromC0.C1records that propertyxis located at offset 0. V8 then updates the object to point toC1. - Transition Link: Crucially, V8 adds a "transition"
pointer on
C0that says: "If propertyxis added to an object with classC0, transition it to classC1." - Second Property: Adding another property
(
obj.y = 20) creates hidden classC2, recordingyat offset 1, and linksC1toC2.
This transition mechanism allows objects to share hidden classes. If
you create a second empty object and add x and
y in the exact same order, V8 will not create new hidden
classes. Instead, it follows the existing transition chain from
C0 to C1 to C2. Both objects now
share the C2 hidden class, saving memory and establishing
that both objects have the exact same structural layout.
The Payoff: Inline Caching
Hidden classes unlock V8's most powerful optimization technique: Inline Caching (IC).
When a function accesses an object's property, V8 observes the hidden
class of the object passed into that function. For example, consider a
function that returns obj.y.
- First Execution: The first time the function runs,
V8 looks up the object's hidden class (
C2), finds thatyis at offset 1, and retrieves the value. - Caching: V8 then modifies the compiled machine code
of the function itself—a process called inline caching. It hardcodes the
expected hidden class (
C2) and the offset (1) directly into the instruction path. - Subsequent Executions: The next time the function
is called, V8 does not look up the property name at all. It simply
checks if the incoming object has the hidden class
C2. If it matches, V8 immediately reads the memory at offset 1.
By combining hidden classes with inline caching, Chromium reduces a complex dynamic property lookup into a single comparison and a direct memory read.
Deoptimization and Property Order
The reliance on transition chains means that the order in which properties are added matters.
If Object A is assigned x then y, and
Object B is assigned y then x, they will
follow different transition paths and end up with two different hidden
classes. Furthermore, the memory offsets for x and
y will be reversed between the two objects.
If both objects are passed into the same function, the inline cache will fail because the hidden classes do not match. If a function receives objects with too many different hidden classes, V8 gives up on inline caching for that operation (a state known as megamorphism) and falls back to slower lookup methods. Maintaining consistent object initialization orders ensures Chromium can reuse hidden classes and keep property access fully optimized.