completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Hidden classes & object shapes

Learn how JavaScript engines use hidden classes, transition trees, descriptor arrays, fast properties, and migrations to speed up objects.

By the end, you can
  • 01
    Explain an object's planSay what a hidden class (a map in V8) holds, and why objects built the same way can share one, so the engine reads values from fixed slots.
  • 02
    Follow maps and storageTrace how adding properties moves an object from map to map, and tell apart in-object slots, the extra list beside an object, and dictionary mode.
  • 03
    Keep V8 facts apart from language rulesPredict what delete, Object.freeze, slack tracking, and a decimal value do in V8 12.4, and know which of these your code can actually see.

Objects can change, but they still get a fixed plan

In JavaScript you can add a property to an object at any time. You can also change it, delete it, or freeze the whole object. That makes objects very flexible.

The engine is the program that runs your JavaScript. Chrome and Node use an engine called V8. The engine wants to read a property like user.name fast. But if any object could hold any names in any order, the engine would have to search the object for name on every read. Searching is slow.

The main idea

A hidden class is the engine's plan for an object. It is "hidden" because your code can't see it. The plan says three things:

  • which property names the object has,
  • the order they were added in,
  • which numbered slot holds each value.

Objects built the same way share one plan. So the engine can read a value straight from its slot, without a search.

Each engine has its own word for this plan. V8 calls it a map. Firefox's engine, SpiderMonkey, calls it a shape. Safari's engine, JavaScriptCore, calls it a structure. They all do the same job. This lesson uses V8's word, map.

The Inline caches lesson showed how one line of code remembers the map it saw last time, so the next read only needs a quick check. This lesson looks inside that map.

Two kinds of facts appear in this lesson. Keep them apart:

  • Language rules are what your code can see, like property order, delete, and freezing. They are the same in every engine.
  • Engine facts are how V8 stores objects inside. Your code can't see them, and they can change between versions. The lesson checks them in Node 22, which uses V8 12.4.

Objects built the same way share a map

Normal JavaScript can't show you a map. But it can show one clue: the order of property names. Object.keys(obj) returns an object's property names, in the order they were added.

Same names in the same order, and in a different orderPop out in the code editor (opens in a new tab)JavaScript
function makeTicket(name) {  return { name, platform: 2, paid: true };} const asha = makeTicket("Asha");const ravi = makeTicket("Ravi");const meera = { platform: 2, name: "Meera", paid: true }; console.log(Object.keys(asha).join(","));console.log(Object.keys(ravi).join(","));console.log(Object.keys(meera).join(","));
  • Lines 1 to 3 make a function, makeTicket. It always builds a ticket with name, then platform, then paid.
  • Lines 5 and 6 build Asha's and Ravi's tickets with it.
  • Line 7 writes Meera's ticket by hand, with platform first.
  • Lines 9 to 11 print name,platform,paid twice, then platform,name,paid.
Two carts, asha and ravi. The asha cart holds the values Asha, 2 and true. The ravi cart holds Ravi, 2 and true. Both point to one shared map that says slot 0 is name, slot 1 is platform, and slot 2 is paid.makeTicket("Asha")Asha2trueashaone map0: name1: platform2: paidmakeTicket("Ravi")Ravi2trueravi
asha and ravi were made by the same function, so they share one map. Each object keeps only its values, and the map says which slot holds which name.

Asha's and Ravi's tickets were built the same way, so V8 gives them one map. Meera's ticket has the same names in a different order, so it gets a different map. Normal code can't check this. The next code can.

V8 has special test functions whose names start with %. For example, %HaveSameMap(a, b) returns true when two objects share one map. These functions only work when you start Node with node --allow-natives-syntax. They are not part of JavaScript, so never use them in a real app.

Node only: check which objects share a mapJavaScript
function Point(x) {  this.x = x;} const a = { x: 1, y: 2 };const b = { x: 3, y: 4 };const c = { y: 2, x: 1 };const p = new Point(1);const q = new Point(2); console.log("node", process.version);console.log("v8", process.versions.v8);console.log("same order", %HaveSameMap(a, b));console.log("different order", %HaveSameMap(a, c));console.log("constructor start", %HaveSameMap(p, q));p.y = 2;console.log("after one transition", %HaveSameMap(p, q));q.y = 3;console.log("after both transition", %HaveSameMap(p, q));
  • Lines 11 and 12 print the Node and V8 versions.
  • Lines 5 and 6 add x, then y. Line 13 prints same order true.
  • Line 7 adds y first. Line 14 prints different order false.
  • Lines 8 and 9 build two objects with the Point constructor. A constructor is a function you call with new to build objects. Line 15 prints constructor start true.
  • Line 16 adds y to p only. Line 17 prints after one transition false.
  • Line 18 adds y to q too. Line 19 prints after both transition true.
What the lesson test saw

The lesson test runs this code in Node 22 with V8 12.4. It prints exactly the results above: the same order shares a map, a different order doesn't, and two Point objects split apart when only one gets y, then share a map again once both have it.

The table below lists each engine's word for a map. Read each row as: this engine, its word, and one thing to remember.

What each engine calls an object's plan
Which engineWhat it calls the planWhat to remember
V8 (Chrome, Node)A map, also called a hidden class.Every object points to its map first. V8 compares maps to check quickly that two objects have the same plan.
SpiderMonkey (Firefox)A shape.It does the same job: it tells property reads and inline caches where each value is.
JavaScriptCore (Safari)A structure.It does the same job inside Safari's engine.

You can read more from the engine teams. The V8 maps documentation explains maps and the lists they point to. Fast properties in V8 explains where property values are stored. Mathias Bynens and Benedikt Meurer's Shapes and Inline Caches compares the words different engines use.

Adding a property moves an object to the next map

A new, empty object starts on a first map with no names. The step-through below calls it Root. When you add a property, the object moves to a new map that has one more name. That move is called a transition.

  • Add name, then score: the object goes from Root to Root.name to Root.name.score.
  • Add score, then name: it goes from Root to Root.score to Root.score.name.

All these moves together form a tree that starts at Root. One path through the tree is a transition chain. Objects that add the same names in the same order follow the same chain, and end on the same map.

Real-life analogyBadges on a school uniform

At a school, every uniform has its badges in the same places. A teacher looks at the left pocket and finds the name badge. If one student sews the badges on in a different order, the teacher needs a different plan to find them quickly.

In real life: A plain uniform with no badges
In JavaScript: The Root map of a new, empty object
In real life: Name badge first, then house badge
In JavaScript: Adding name, then score
In real life: House badge first, then name badge
In JavaScript: Adding score first: a different branch

The step-through is a small model written in plain JavaScript. It isn't V8, but it follows the same rule. Each record keeps its map name as text in record.map. transitions is a JavaScript Map, a list of keys and values. Here it remembers every move made so far, so nextMap reuses a move another record already made.

Step through: three objects move from map to map
Step 0 of 9Ready
Your turn: follow the blue line

Follow Asha, Ravi, and Meera as each new property moves them to the next map. This is a small model in plain JavaScript, not V8 itself.

Running in
  1. script
Next: line 18
const transitions = new Map();function nextMap(current, property) {  const key = current + "->" + property;  if (!transitions.has(key)) transitions.set(key, current + "." + property);  return transitions.get(key);} function makeRecord(label) {  return { label, map: "Root", slots: {} };} function store(record, property, value) {  record.map = nextMap(record.map, property);  record.slots[property] = value;  return record;} store(asha, "name", "Asha");store(asha, "score", 10);const ravi = makeRecord("Ravi");store(ravi, "name", "Ravi");store(ravi, "score", 20);const meera = makeRecord("Meera");store(meera, "score", 30);store(meera, "name", "Meera"); console.log(asha.map === ravi.map);console.log(asha.map === meera.map);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Lines 19 and 20 move Asha along the chain Root.name, then Root.name.score. Ravi follows the same chain on lines 22 and 23, so line 28 prints true. Meera adds score first on lines 25 and 26, so line 29 prints false.

A map tree. From the Root map, adding name leads to Root.name, then adding score leads to Root.name.score, where Asha and Ravi end. From Root, adding score leads to Root.score, then adding name leads to Root.score.name, where Meera ends.Rootempty+ nameRoot.name+ scoreRoot.name.scoreAsha, Ravi+ scoreRoot.score+ nameRoot.score.nameMeera
Each new property moves an object one step to the next map. Asha and Ravi add name first and share a map, but Meera adds score first and ends on another branch.

Now build your own tree. Add x, y, or z to a new object in any order, then save it and build another. The map names M0, M1, and so on are numbers the model makes up as it creates each map.

Playground: build a map tree
The model: objects built in two ordersJavaScript
const asha = objectWith(["x", "y"]);const ravi = objectWith(["x", "y"]);const meera = objectWith(["y", "x"]); console.log(asha.map === ravi.map);console.log(asha.map === meera.map);console.log(treeFromRoot());
Map tree0 objects

No objects yet. Add a name to the new object to make the first move.

  1. emptyM0

    no names yet

Step 0 of 41 maps

Add x, y, or z to the new object. Then save it, and build another object in the same order or a different one.

This is a small model in plain JavaScript. It doesn't look inside your browser's engine. It builds a map tree from the orders you choose.

Descriptor arrays: the list of names behind a map

A map points to a list with one entry for each property. Each entry is called a descriptor. A descriptor holds:

  • the property name,
  • its settings, like whether it can be changed (the property descriptors lesson covers these),
  • what kind of value it holds, like a small whole number,
  • the slot where its value lives.

V8 keeps these entries in a list called a DescriptorArray. Maps on one chain can share one descriptor array. The map for { x } and the map for { x, y } can use the same array. The first map just reads fewer entries: only the first one.

The next code uses another V8 test function, %DebugPrint(obj). It prints V8's own report about an object, including its map and the address of its descriptor array. An address is the place in memory where something is stored.

Node only: two maps on one chain share a descriptor arrayJavaScript
const one = {};one.x = 1;const two = {};two.x = 1;two.y = 2; console.log("ONE");%DebugPrint(one);console.log("TWO");%DebugPrint(two);
  • Lines 1 and 2 give one the map for { x }.
  • Lines 3 to 5 give two the map for { x, y }, one step further along the same chain.
  • Lines 8 and 10 print V8's report for each object.
What the lesson test saw

The test reads both reports. Both maps name the same DescriptorArray[2] address, which is a list with 2 entries. The address is different on every run, so the test only checks that the two addresses match.

This is why the name "hidden class" can mislead. It isn't a class you write in JavaScript. It is a small record inside the engine, and maps on the same chain share parts of it.

Where property values are stored

V8 keeps two kinds of properties apart:

  • Named properties, like user.name. This section is about these.
  • Elements, the numbered entries of an array, like list[0]. The next lesson covers those.

A named property's value can live in one of three places:

  • In the object itself ("in-object"). This is the fastest place.
  • In an extra list beside the object, called the properties backing store. The map still says which slot holds which name, so this is fast too.
  • In a dictionary: the object keeps its own table of names and values, with no shared map to say where things are. This is flexible, but slower to read.

The first two are called fast properties. The dictionary kind is called slow properties, or dictionary mode.

Normal JavaScript shows the names, not where they are storedPop out in the code editor (opens in a new tab)JavaScript
const fast = { name: "Asha", score: 0, active: true };const extended = { name: "Ravi", score: 0, active: true };extended.city = "Pune"; console.log(Object.keys(fast).join(","));console.log(Object.keys(extended).join(","));console.log("city" in extended);
  • Lines 1 and 2 build two objects with the same three names.
  • Line 3 adds city to extended afterwards.
  • Lines 5 and 6 print the names. Line 7 prints true, because "city" in extended checks whether the property exists.

Nothing in this output says where V8 stored city. That stays hidden.

The table below lists the three places. Read each row as: where the value lives, what that means, and why V8 uses it.

The three places a named property's value can live
Where the value livesWhat it meansWhy V8 uses it
In the object itself (in-object)The value sits inside the object, in a fixed slot.It is the fastest place. But an object can't grow in place forever.
In an extra list beside the object (backing store)The value sits in a separate list. The map still says which slot holds which name.It is still fast, because the map says exactly where to look.
In a dictionary (slow properties)The object keeps its own table of names and values.It handles deletes and many new names well, but it is slower to read than fixed slots.

The next code uses one more V8 test function, %HasFastProperties(obj). It returns true when the object uses fast properties, and false when it uses a dictionary.

Node only: which objects keep fast propertiesJavaScript
const removedMiddle = { a: 1, b: 2, c: 3 };console.log("fast before delete", %HasFastProperties(removedMiddle));delete removedMiddle.b;console.log("fast after delete middle", %HasFastProperties(removedMiddle)); const removedLast = { a: 1, b: 2, c: 3 };delete removedLast.c;console.log("fast after delete last", %HasFastProperties(removedLast)); const many = {};for (let i = 0; i < 80; i += 1) many["p" + i] = i;console.log("fast after many", %HasFastProperties(many)); const frozen = { a: 1, b: 2 };console.log("fast before freeze", %HasFastProperties(frozen));Object.freeze(frozen);console.log("fast after freeze", %HasFastProperties(frozen));console.log("is frozen", Object.isFrozen(frozen));
What the lesson test saw

In Node 22 with V8 12.4, the test saw these results:

  • A new object is fast (line 2).
  • After deleting the middle property, it is not fast (line 4).
  • After deleting the last property, it is not fast either (line 8).
  • After adding 80 properties in a loop, it is not fast (line 12).
  • A frozen object with two properties is still fast (line 17).

Slack tracking trims spare space

When you build objects with a constructor, V8 doesn't know yet how many properties they will end up with. So the first objects get some spare in-object slots, just in case. This spare room is called slack.

After a number of objects have been built, V8 looks at how many slots were really used. Then it trims the spare ones from the map. This is slack tracking. V8's slack tracking post says the counter starts at about seven constructions.

Real-life analogyA new notebook leaves blank lines

In a new notebook, you leave a few blank lines under each topic in case you need more notes. After a few pages, you know how much space you really use, so you stop leaving blank lines.

In real life: The first pages leave blank lines
In JavaScript: The first objects get spare slots
In real life: Later you only leave the lines you need
In JavaScript: V8 trims the spare slots from the map
In real life: Notes you wrote stay where they are
In JavaScript: Properties keep their slots
In real life: Extra notes go on a separate page
In JavaScript: Extra properties go to the backing store
Node only: early objects have spare slots, later ones don'tJavaScript
function Seat(name) {  this.name = name;  this.paid = true;} const first = new Seat("Asha");console.log("FIRST");%DebugPrint(first); for (let i = 0; i < 12; i += 1) new Seat("warm" + i);const later = new Seat("Ravi");console.log("LATER");%DebugPrint(later);console.log("same map after trim", %HaveSameMap(first, later));
  • Lines 1 to 4 make the Seat constructor. It gives every seat two properties.
  • Line 6 builds the first seat, and line 8 prints V8's report about it.
  • Line 10 builds 12 more seats, which ends the warm-up.
  • Line 11 builds one more seat, and line 13 prints its report.
  • Line 14 checks whether first and later share a map.
What the lesson test saw

The test compares the two reports. The first seat has more in-object slots, and some of them are unused. The later seat has fewer in-object slots, and 0 unused ones. Line 14 still prints true: both seats share one map, because V8 trimmed that map itself.

Dictionary mode, delete, and freeze

In dictionary mode, an object keeps its own table of names and values, instead of using a shared map's slots. This isn't an error or a punishment. V8 picks it when fixed slots stop being a good fit:

  • after you delete a named property, which leaves a gap in the plan,
  • after you add many different names to one object, one by one.

Freezing is different. Object.freeze(obj) makes an object read-only: nothing can be added, removed, or changed. V8 moves a frozen object to a new map, but a small frozen object can keep fast properties.

What your code sees: delete removes a property, freeze locks an objectPop out in the code editor (opens in a new tab)JavaScript
const row = { id: 1, name: "Asha", city: "Pune" };delete row.city; const settings = { theme: "dark", compact: false };Object.freeze(settings); console.log(Object.keys(row).join(","));console.log(Object.isFrozen(settings));console.log(settings.theme);
  • Line 2 removes city from row.
  • Line 5 freezes settings.
  • Line 7 prints id,name.
  • Line 8 prints true. Object.isFrozen checks whether an object is frozen.
  • Line 9 prints dark. Freezing keeps the values that were already there.
Left, before delete: the row cart holds the values 1, Asha and Pune and points to a shared map with the names id, name and city. Right, after delete row.city: the row cart holds its own list, id: 1 and name: Asha, with no shared map.before delete1AshaPunerowidnamecityshares a mapdelete row.cityid: 1name: Asharowits own list of names
Before the delete, row uses a shared map. After delete row.city, row keeps its own list of names and values, which is slower to read.
Node only: fast properties after delete and freezeJavaScript
const removed = { id: 1, name: "Asha", city: "Pune" };delete removed.city;console.log("delete stays fast", %HasFastProperties(removed)); const settings = { theme: "dark", compact: false };console.log("freeze before", %HasFastProperties(settings));Object.freeze(settings);console.log("freeze after", %HasFastProperties(settings));console.log("same map as unfrozen", %HaveSameMap({ theme: "dark", compact: false }, settings));
  • Line 3 prints delete stays fast false: after the delete, the object uses a dictionary.
  • Lines 6 and 8 print true before and after the freeze: the frozen object is still fast.
  • Line 9 compares settings with a new unfrozen object that has the same names. It prints same map as unfrozen false: freezing moved settings to a new map.

The next code prints V8's full report for a deleted object and for a frozen object. The lesson test looks for a few words in them:

  • For the deleted object: dictionary_map and NameDictionary. A NameDictionary is V8's table of names and values.
  • For the frozen object: non-extensible, which means no new properties can be added, and stable_map.
Node only: V8's reports for a deleted and a frozen objectJavaScript
const dictionary = { a: 1, b: 2, c: 3 };delete dictionary.b;console.log("DICTIONARY");%DebugPrint(dictionary); const frozen = { a: 1, b: 2 };Object.freeze(frozen);console.log("FROZEN");%DebugPrint(frozen);

The table below sums up these tests. Read each row as: what you do, what V8 12.4 did, and what the test saw.

What delete, many new names, and freeze did in V8 12.4
What you doWhat V8 12.4 didWhat the test saw
delete obj.b, a middle propertyThe object moved to a dictionary.%HasFastProperties printed false.
delete obj.c, the last propertyThe object moved to a dictionary too.%HasFastProperties printed false, so don't count on deleting the last property being safe.
Add 80 names, one by oneThe object moved to a dictionary.%HasFastProperties printed false.
Object.freeze(obj)The object moved to a new, frozen map, but stayed fast.%HasFastProperties printed true, and %HaveSameMap with an unfrozen copy printed false.
Fast, dictionary, or frozen but fast?
  • A constructor always adds name, then score, then active.
  • Code your app uses a lot deletes city from a row after building it.
  • Object.freeze is called on a small settings object that already has all its properties.
  • A plugin object gets 80 different property names while the program runs.
  • Two objects are written as { x, y }, in the same order.
  • A finished settings object is frozen before other code uses it.
Try it yourself
0 of 6 correct

Sort each object by how V8 12.4 stores it, based on the tests in this lesson.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Old maps get replaced: deprecation and migration

A map also remembers what kind of value each property holds. V8 has two kinds of numbers here:

  • a Smi (short for "small integer") is a small whole number, like 1;
  • a Double is a number with a decimal part, like 1.5.

A Smi is cheaper to store. So a new { x: 1 } gets a map that says "x is a Smi".

Now store 1.5 in x. It doesn't fit. V8 makes a new map that says "x is a Double" and moves the object there. The old map is marked deprecated, which means outdated.

Other objects still on the old map are not moved right away. Each one moves the next time it is used. That move is called migration.

The step-through is a model in plain JavaScript. Each object keeps its map name as text, like "M1:x=Smi".

Step through: a decimal replaces a map
Step 0 of 9Ready
Your turn: follow the blue line

Follow a model of an outdated map: a decimal moves b to a new map, and a moves the next time it is used.

Running in
  1. script
Next: line 1
const b = { map: "M1:x=Smi", x: 2 };console.log(a.map === b.map); b.x = 1.5;b.map = "M2:x=Double";const deprecated = "M1:x=Smi";console.log(a.map === b.map); if (a.map === deprecated) {  a.x = Number(a.x);  a.map = "M2:x=Double";}console.log(a.map === b.map);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
  • Line 3 prints true: a and b start on M1.
  • Line 5 stores 1.5 in b.x, and line 6 moves b to M2. Line 8 prints false.
  • Line 10 notices that a is still on the outdated map, and line 12 moves it. Line 14 prints true.
Cart a holds x: 1 and points to the old map M1:x=Smi. Cart b holds x: 1.5; after b.x = 1.5 it points to the new map M2:x=Double. An arrow from the old map to the new map says a moves on its next use.x: 1aM1:x=Smiold map, now outdatedx: 1.5bb.x = 1.5M2:x=Doublenew map: x can be a decimala moves onits next use
The decimal 1.5 doesn't fit the old map, so b moves to a new map. Object a moves there too, the next time it is used.

The real V8 code below does the same with real objects.

Node only: a decimal replaces a map, and a read moves the other objectJavaScript
const a = { x: 1 };const b = { x: 2 };console.log("start same", %HaveSameMap(a, b)); b.x = 1.5;console.log("after double same", %HaveSameMap(a, b)); console.log("A BEFORE");%DebugPrint(a);console.log("access", a.x);console.log("A AFTER");%DebugPrint(a);console.log("after access same", %HaveSameMap(a, b));
  • Lines 1 to 3 build a and b, and print start same true.
  • Line 5 stores 1.5 in b.x. Line 6 prints after double same false.
  • Line 9 prints V8's report for a before it is used.
  • Line 10 reads a.x. That read is the use that moves a.
  • Line 12 prints the report again. Line 13 prints after access same true.
What the lesson test saw

The first report for a says deprecated_map: its map is outdated. After the read, the report says stable_map and migration_target: a is now on the map that objects move to. %HaveSameMap(a, b) goes from true, to false, and back to true.

Habits that keep objects fast

Most of the time, you don't need to think about maps at all. A few simple habits are enough. The code below shows the first two.

Give every object all its properties from the startPop out in the code editor (opens in a new tab)JavaScript
function makeCard(name, city) {  return {    name,    city,    selected: false,    error: null,  };} const first = makeCard("Asha", "Pune");const second = makeCard("Ravi", "Kochi");second.error = "missing phone";first.error = null; console.log(Object.keys(first).join(","));console.log(Object.keys(second).join(","));
  • Lines 1 to 8 make makeCard. It builds every card with the same four properties, in the same order.
  • error starts as null, which means "no error yet".
  • Lines 10 and 11 build two cards. Line 12 sets Ravi's error to a message.
  • Line 13 sets Asha's error to null instead of deleting it.
  • Lines 15 and 16 print the same names for both cards: name,city,selected,error.
  • Build similar objects with one function or constructor, so they get the same properties in the same order.
  • To clear a property on an object your code uses a lot, set it to null or undefined instead of deleting it.
  • Measure first. Change clear code only when a profiler, a tool that shows where your program spends its time, points at a slow spot.
  • For related topics, see inline caches, prototype internals, property descriptors, and freezing and sealing.

Common misconceptions

  • "Hidden classes are JavaScript classes." They are plans inside the engine. They have nothing to do with the class keyword.
  • "The same property names always mean the same map." The order matters too. So do each property's settings, the kind of value it holds, and the object's prototype.
  • "Deleting the last property keeps an object fast." Not in V8 12.4. The test deleted the middle property and the last one, and both objects stopped being fast.
  • "Freezing makes objects slow." Freezing moves the object to a new map, but a small frozen object can keep fast properties.
  • "Engine tricks matter more than clear code." Change code for maps only when a measurement shows a slow spot.

The table below lists four ideas that are easy to mix up. Read each row as: the idea, what it means, and what it is not.

Four ideas that are easy to mix up
IdeaWhat it meansWhat it is not
Language rulesWhat your code can see: properties, prototypes, property settings, delete, and freezing.Not hidden classes. The JavaScript standard never mentions them.
Hidden class, map, or shapeThe engine's plan for an object: its names, their settings, the kind of value each holds, and their slots.Not class User {}. Your code can't see it.
Inline cacheA small memory at one line of code. It remembers the map it saw last time.Not the map itself. It only checks the map; the inline caches lesson covers it.
Dictionary modeThe object keeps its own table of names and values, instead of fixed slots.Not a bug in your code. It only matters in code a profiler shows is slow.

Practice exercises

Exercise 1 · Warm-upPredict the two map checks

Read the model code. Type the two values that the last two lines print.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const transitions = new Map();
function nextMap(current, property) {
  const key = current + "->" + property;
  if (!transitions.has(key)) transitions.set(key, current + "." + property);
  return transitions.get(key);
}

function makeRecord(label) {
  return { label, map: "Root", slots: {} };
}

function store(record, property, value) {
  record.map = nextMap(record.map, property);
  record.slots[property] = value;
  return record;
}

const asha = makeRecord("Asha");
store(asha, "name", "Asha");
store(asha, "score", 10);
const ravi = makeRecord("Ravi");
store(ravi, "name", "Ravi");
store(ravi, "score", 20);
const meera = makeRecord("Meera");
store(meera, "score", 30);
store(meera, "name", "Meera");

console.log(asha.map === ravi.map);
console.log(asha.map === meera.map);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upName the list behind a map

    What does V8 call the list that holds each property's name, settings, and slot for a map?

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeRead the delete test

      What does the lesson's V8 code print after delete stays fast?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeWrite a function that builds objects the same way

        Run the starter code. In what order does it print the property names?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function makeRow(name) {
          return { name, score: 0, active: true };
        }
        console.log(Object.keys(makeRow("Asha")).join(","));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · PracticeTrace a migration

          Which three values does the migration model print?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const a = { map: "M1:x=Smi", x: 1 };
          const b = { map: "M1:x=Smi", x: 2 };
          console.log(a.map === b.map);
          
          b.x = 1.5;
          b.map = "M2:x=Double";
          const deprecated = "M1:x=Smi";
          console.log(a.map === b.map);
          
          if (a.map === deprecated) {
            a.x = Number(a.x);
            a.map = "M2:x=Double";
          }
          console.log(a.map === b.map);

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeUse it in a real app

            Name one habit you would use for objects in a list that a profiler showed is slow.

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, first ask: is this a language rule your code can see, or a V8 fact? For code questions, run the lines in order in your head.

              Hidden classes quiz · 8 questionsScore: first tries count
              1. Question 1 of 8What is a hidden class, or map?

                Choose an answer to see the explanation.

              2. Question 2 of 8What does this code print about name order?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const a = { x: 1, y: 2 };
                const b = { y: 2, x: 1 };
                console.log(Object.keys(a).join("|"));
                console.log(Object.keys(b).join("|"));

                Choose an answer to see the explanation.

              3. Question 3 of 8Why do maps form a tree?

                Choose an answer to see the explanation.

              4. Question 4 of 8Which sentence about descriptor arrays is true?

                Choose an answer to see the explanation.

              5. Question 5 of 8What does this freeze code print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const settings = { theme: "dark" };
                Object.freeze(settings);
                console.log(Object.isFrozen(settings));
                console.log(settings.theme);

                Choose an answer to see the explanation.

              6. Question 6 of 8In the V8 12.4 test, what happened after delete removed a property?

                Choose an answer to see the explanation.

              7. Question 7 of 8What does this map model print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const a = { map: "M1:x=Smi", x: 1 };
                const b = { map: "M1:x=Smi", x: 2 };
                console.log(a.map === b.map);
                b.x = 1.5;
                b.map = "M2:x=Double";
                const deprecated = "M1:x=Smi";
                console.log(a.map === b.map);
                if (a.map === deprecated) a.map = "M2:x=Double";
                console.log(a.map === b.map);

                Choose an answer to see the explanation.

              8. Question 8 of 8Which habit for real apps follows from this lesson?

                Choose an answer to see the explanation.

              Key takeaways

              • A hidden class is the engine's plan for an object. V8 calls it a map, SpiderMonkey a shape, and JavaScriptCore a structure.
              • Adding a property moves an object to the next map. The same names in a different order end on a different map.
              • A map points to a descriptor array, and maps on one chain can share it.
              • Fast properties use fixed slots. Dictionary mode, used after a delete or many new names, uses the object's own table.
              • Slack tracking trims spare slots from early objects. A value that doesn't fit, like a decimal, can replace a map, and old objects move later.
              • Build similar objects the same way, avoid delete on objects you use a lot, and measure before you optimize.

              Remember the one-liner.
              A hidden class is the engine's shared plan for objects that were built the same way.

              You came from Symbols inside the engine. Up next: Fast arrays & elements kinds, where arrays get their own plans and transitions.

              CompleteFrontend Clear concepts. Working examples.