Stop Breaking V8: How Hidden Classes and Inline Caching Impact JS Performance
Learn how V8's Hidden Classes and Inline Caching optimize JavaScript property access, and why initialization order is critical for performance.
10 Sept 2026, 21:06 UTC

The Cost of Dynamic Objects
\nJavaScript is a dynamic language, meaning you can add or remove properties from an object at any time. While this flexibility is convenient, it is computationally expensive. In a naive implementation, every time you access user.name, the engine would have to perform a dictionary lookup to find where name is stored in memory.
To solve this, the V8 engine (used in Chrome and Node.js) uses Hidden Classes (also known as Shapes) and Inline Caching (IC). The takeaway for developers is simple: The order in which you initialize your object properties determines whether your code runs at peak speed or falls back to a slow, generic lookup.
\n\nHow Hidden Classes Work
\nV8 doesn't store a map of property names to values inside every single object. Instead, it assigns a Hidden Class to each object. A Hidden Class stores the offset of each property in memory.
\nWhen you create an empty object, it starts with an initial Hidden Class. As you add properties, V8 creates a transition tree. If you add x and then y, the object moves from Class 0 → Class X → Class XY. Any other object that follows this exact same sequence of additions will share the same Hidden Class.
Accelerating Access with Inline Caching (IC)
\nHidden Classes alone aren't enough; the engine still has to check the class to find the offset. This is where Inline Caching comes in. V8 remembers the result of a property lookup at a specific location in your code (the call site).
\n- \n
- Monomorphic: The call site has seen only one Hidden Class. V8 skips the lookup and jumps straight to the memory offset. This is the fastest state. \n
- Polymorphic: The call site has seen a few different Hidden Classes. V8 performs a quick check against a small list of known classes. \n
- Megamorphic: The call site has seen too many different classes. V8 gives up on caching and reverts to a slow, generic dictionary lookup. \n
Worked Example: Initialization Order
\nConsider a function that processes a list of point objects. The following example demonstrates how initialization order affects the Hidden Class.
\n// Consistent initialization: Monomorphic\nconst p1 = {};\np1.x = 1;\np1.y = 2;\n\nconst p2 = {};\np2.x = 3;\np2.y = 4;\n\nfunction getX(p) {\n return p.x;\n}\n\n// Both p1 and p2 share the same Hidden Class (Class 0 -> Class X -> Class XY)\n// The 'getX' call site remains monomorphic.\ngetX(p1);\ngetX(p2);\n\n// Inconsistent initialization: Polymorphic\nconst p3 = {};\np3.y = 5;\np3.x = 6;\n\n// p3 has a different Hidden Class (Class 0 -> Class Y -> Class YX)\n// Calling getX(p3) forces the call site to become polymorphic.\ngetX(p3);\n\nIn the first case, V8 knows exactly where x is for every object passed to getX. In the second case, V8 must check which class the object belongs to before it can determine the offset for x.
The Danger of the 'delete' Operator
\nUsing the delete keyword is one of the fastest ways to degrade performance. When you delete a property, V8 often decides that the object is too unstable to track with a Hidden Class. It transitions the object into dictionary mode (also called hash table mode).
Once an object is in dictionary mode, it no longer uses Hidden Classes for property access. Every access becomes a key-value lookup, which is significantly slower than the offset-based access used for stable shapes.
\n\nLimitations and Verification
\nThese optimizations are internal to V8 and can vary between versions. Furthermore, if you are using TypeScript or modern ES6 classes, the engine can often optimize the layout more effectively because the \"shape\" is defined upfront in the constructor.
\nTo verify if your objects are sharing shapes, you can use the Chrome DevTools Memory tab:
\n- \n
- Take a Heap Snapshot. \n
- Search for your object constructors or instances. \n
- Inspect the
# shapeor# hidden classproperty in the object details to see if multiple instances share the same internal ID. \n
Practical Closing
\nTo keep your JavaScript execution fast, follow these three rules: initialize properties in the same order across all instances of a similar object, avoid adding properties to objects after they have been created, and prefer setting a property to null or undefined rather than using delete.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.