javascript
examples
examples.jsā”javascript
/**
* ============================================================
* 22.2 HIDDEN CLASSES & INLINE CACHING - EXAMPLES
* ============================================================
*
* Demonstrations of hidden class behavior and inline caching.
*/
/**
* ============================================================
* EXAMPLE 1: SHARED HIDDEN CLASSES
* ============================================================
*/
console.log('=== Example 1: Shared Hidden Classes ===\n');
// All these objects share the same hidden class
function createPoint(x, y) {
return { x, y };
}
const p1 = createPoint(1, 2);
const p2 = createPoint(3, 4);
const p3 = createPoint(5, 6);
console.log('Three points with same structure:');
console.log('p1:', p1);
console.log('p2:', p2);
console.log('p3:', p3);
console.log('These share the same hidden class (x @ offset 0, y @ offset 8)\n');
/**
* ============================================================
* EXAMPLE 2: HIDDEN CLASS TRANSITIONS
* ============================================================
*/
console.log('=== Example 2: Hidden Class Transitions ===\n');
// Watch how hidden class changes as properties are added
const obj = {};
console.log('Step 1: Empty object {}');
console.log(' Hidden Class: C0 (empty)');
obj.a = 1;
console.log("Step 2: Added 'a':", obj);
console.log(" Transition: C0 ā C1 (has 'a' @ offset 0)");
obj.b = 2;
console.log("Step 3: Added 'b':", obj);
console.log(" Transition: C1 ā C2 (has 'a' @ 0, 'b' @ 8)");
obj.c = 3;
console.log("Step 4: Added 'c':", obj);
console.log(" Transition: C2 ā C3 (has 'a' @ 0, 'b' @ 8, 'c' @ 16)\n");
/**
* ============================================================
* EXAMPLE 3: PROPERTY ORDER MATTERS
* ============================================================
*/
console.log('=== Example 3: Property Order Matters ===\n');
// These have DIFFERENT hidden classes despite same properties
const orderA = {};
orderA.x = 1;
orderA.y = 2;
const orderB = {};
orderB.y = 2; // Different order!
orderB.x = 1;
console.log('orderA (x then y):', orderA);
console.log('orderB (y then x):', orderB);
console.log('\nā ļø These have DIFFERENT hidden classes!');
console.log('orderA: x @ offset 0, y @ offset 8');
console.log('orderB: y @ offset 0, x @ offset 8\n');
// Demonstrating the issue
function getX(obj) {
return obj.x;
}
// If we call getX with both, it becomes polymorphic
console.log('getX(orderA):', getX(orderA), '- IC sees shape A');
console.log('getX(orderB):', getX(orderB), '- IC now polymorphic (2 shapes)\n');
/**
* ============================================================
* EXAMPLE 4: CONSISTENT OBJECT CREATION
* ============================================================
*/
console.log('=== Example 4: Consistent Object Creation ===\n');
// ā BAD: Inconsistent creation
function createUserBad(data) {
const user = {};
if (data.name) user.name = data.name;
if (data.email) user.email = data.email;
if (data.age) user.age = data.age;
return user;
}
// These all have different hidden classes!
const user1 = createUserBad({ name: 'Alice' });
const user2 = createUserBad({ name: 'Bob', email: 'bob@test.com' });
const user3 = createUserBad({
age: 25,
name: 'Carol',
email: 'carol@test.com',
});
console.log('ā BAD: Different shapes');
console.log('user1:', user1, '- Shape: {name}');
console.log('user2:', user2, '- Shape: {name, email}');
console.log('user3:', user3, '- Shape: {age, name, email}');
// ā
GOOD: Consistent creation
function createUserGood(data) {
return {
name: data.name || null,
email: data.email || null,
age: data.age || null,
};
}
const goodUser1 = createUserGood({ name: 'Alice' });
const goodUser2 = createUserGood({ name: 'Bob', email: 'bob@test.com' });
const goodUser3 = createUserGood({
age: 25,
name: 'Carol',
email: 'carol@test.com',
});
console.log('\nā
GOOD: Same shape (all have name, email, age)');
console.log('goodUser1:', goodUser1);
console.log('goodUser2:', goodUser2);
console.log('goodUser3:', goodUser3);
console.log();
/**
* ============================================================
* EXAMPLE 5: INLINE CACHING STATES
* ============================================================
*/
console.log('=== Example 5: Inline Caching States ===\n');
// MONOMORPHIC: Only one shape
function getValue_Mono(obj) {
return obj.value;
}
const monoObjects = [];
for (let i = 0; i < 1000; i++) {
monoObjects.push({ value: i }); // All same shape
}
console.log('MONOMORPHIC example:');
console.log('All 1000 objects have same shape: { value }');
monoObjects.forEach((o) => getValue_Mono(o));
console.log('IC state: MONOMORPHIC (fastest)\n');
// POLYMORPHIC: 2-4 shapes
function getValue_Poly(obj) {
return obj.value;
}
console.log('POLYMORPHIC example:');
getValue_Poly({ value: 1 });
console.log('Call 1: { value } - IC: MONOMORPHIC');
getValue_Poly({ value: 2, extra: 'a' });
console.log('Call 2: { value, extra } - IC: POLYMORPHIC (2 shapes)');
getValue_Poly({ other: true, value: 3 });
console.log('Call 3: { other, value } - IC: POLYMORPHIC (3 shapes)');
getValue_Poly({ value: 4, x: 1, y: 2 });
console.log('Call 4: { value, x, y } - IC: POLYMORPHIC (4 shapes)\n');
// MEGAMORPHIC: 5+ shapes
function getValue_Mega(obj) {
return obj.value;
}
console.log('MEGAMORPHIC example:');
getValue_Mega({ value: 1 });
getValue_Mega({ value: 2, a: 1 });
getValue_Mega({ value: 3, b: 1 });
getValue_Mega({ value: 4, c: 1 });
getValue_Mega({ value: 5, d: 1 });
getValue_Mega({ value: 6, e: 1 }); // 6th shape!
console.log('Called with 6 different shapes');
console.log('IC state: MEGAMORPHIC (slow - dictionary lookup)\n');
/**
* ============================================================
* EXAMPLE 6: PERFORMANCE IMPACT DEMONSTRATION
* ============================================================
*/
console.log('=== Example 6: Performance Impact ===\n');
// Create test data
const ITERATIONS = 1000000;
// Monomorphic test data - all same shape
const monoData = [];
for (let i = 0; i < 1000; i++) {
monoData.push({ x: i, y: i * 2 });
}
// Polymorphic test data - 4 shapes
const polyData = [];
for (let i = 0; i < 250; i++) {
polyData.push({ x: i });
polyData.push({ x: i, a: 1 });
polyData.push({ x: i, b: 2 });
polyData.push({ x: i, c: 3 });
}
// Megamorphic test data - many shapes
const megaData = [];
for (let i = 0; i < 100; i++) {
for (let j = 0; j < 10; j++) {
const obj = { x: i };
obj['prop' + j] = j; // Different property each time
megaData.push(obj);
}
}
function accessProperty(obj) {
return obj.x;
}
// Warmup
monoData.forEach(accessProperty);
// Benchmark monomorphic
const startMono = performance.now();
for (let i = 0; i < ITERATIONS; i++) {
accessProperty(monoData[i % monoData.length]);
}
const monoTime = performance.now() - startMono;
// Reset IC by creating new function
function accessPropertyPoly(obj) {
return obj.x;
}
// Warmup polymorphic
polyData.forEach(accessPropertyPoly);
// Benchmark polymorphic
const startPoly = performance.now();
for (let i = 0; i < ITERATIONS; i++) {
accessPropertyPoly(polyData[i % polyData.length]);
}
const polyTime = performance.now() - startPoly;
// Reset IC
function accessPropertyMega(obj) {
return obj.x;
}
// Warmup megamorphic
megaData.forEach(accessPropertyMega);
// Benchmark megamorphic
const startMega = performance.now();
for (let i = 0; i < ITERATIONS; i++) {
accessPropertyMega(megaData[i % megaData.length]);
}
const megaTime = performance.now() - startMega;
console.log(`MONOMORPHIC: ${monoTime.toFixed(2)}ms (baseline)`);
console.log(
`POLYMORPHIC: ${polyTime.toFixed(2)}ms (${(polyTime / monoTime).toFixed(
2
)}x slower)`
);
console.log(
`MEGAMORPHIC: ${megaTime.toFixed(2)}ms (${(megaTime / monoTime).toFixed(
2
)}x slower)`
);
console.log();
/**
* ============================================================
* EXAMPLE 7: DELETE OPERATOR IMPACT
* ============================================================
*/
console.log('=== Example 7: Delete Operator Impact ===\n');
// ā Using delete - causes dictionary mode
function createWithDeleteBad() {
const obj = { a: 1, b: 2, c: 3, temp: 'temporary' };
delete obj.temp; // Transitions to slow mode!
return obj;
}
// ā
Using null instead
function createWithNullGood() {
const obj = { a: 1, b: 2, c: 3, temp: 'temporary' };
obj.temp = null; // Keeps fast mode
return obj;
}
// ā
Never adding temporary property
function createOptimalBest() {
const obj = { a: 1, b: 2, c: 3 };
// Process temp separately without adding to object
return obj;
}
const objWithDelete = createWithDeleteBad();
const objWithNull = createWithNullGood();
const objOptimal = createOptimalBest();
console.log('After delete:', objWithDelete, '- ā ļø Slow (dictionary mode)');
console.log('With null:', objWithNull, '- ā
Fast (keeps shape)');
console.log('Optimal:', objOptimal, '- ā
Best (no extra property)\n');
// Performance test
function accessProps(obj) {
return obj.a + obj.b + obj.c;
}
const withDeleteArr = Array.from({ length: 1000 }, () => createWithDeleteBad());
const withNullArr = Array.from({ length: 1000 }, () => createWithNullGood());
// Warmup
withDeleteArr.forEach(accessProps);
withNullArr.forEach(accessProps);
const iters = 100000;
const startDelete = performance.now();
for (let i = 0; i < iters; i++) {
accessProps(withDeleteArr[i % 1000]);
}
const deleteTime = performance.now() - startDelete;
const startNull = performance.now();
for (let i = 0; i < iters; i++) {
accessProps(withNullArr[i % 1000]);
}
const nullTime = performance.now() - startNull;
console.log('Property access performance:');
console.log(`With delete: ${deleteTime.toFixed(2)}ms`);
console.log(`With null: ${nullTime.toFixed(2)}ms`);
console.log(`delete is ${(deleteTime / nullTime).toFixed(2)}x slower\n`);
/**
* ============================================================
* EXAMPLE 8: CLASS-BASED CONSISTENT SHAPES
* ============================================================
*/
console.log('=== Example 8: Classes for Consistent Shapes ===\n');
// Classes ensure consistent hidden classes
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
constructor(x, y, z) {
super(x, y);
this.z = z;
}
}
const classPoints = [];
for (let i = 0; i < 1000; i++) {
classPoints.push(new Point(i, i * 2));
}
console.log('1000 Point instances created');
console.log('All share same hidden class from constructor');
console.log('Sample:', classPoints[0], classPoints[500], classPoints[999]);
// Method access is also optimized
class Counter {
constructor(initial = 0) {
this.value = initial;
}
increment() {
return ++this.value;
}
decrement() {
return --this.value;
}
}
const counters = Array.from({ length: 100 }, (_, i) => new Counter(i));
console.log('\n100 Counter instances - all same shape');
counters[0].increment();
counters[1].increment();
console.log('Counter[0]:', counters[0].value);
console.log('Counter[1]:', counters[1].value);
console.log();
/**
* ============================================================
* EXAMPLE 9: MAP FOR DYNAMIC KEYS
* ============================================================
*/
console.log('=== Example 9: Map for Dynamic Keys ===\n');
// ā BAD: Dynamic keys on objects
function cacheWithObjectBad() {
const cache = {};
// Each unique key creates a hidden class transition!
for (let i = 0; i < 100; i++) {
cache['key_' + i] = 'value_' + i;
}
return cache;
}
// ā
GOOD: Use Map for dynamic keys
function cacheWithMapGood() {
const cache = new Map();
// Map handles dynamic keys efficiently
for (let i = 0; i < 100; i++) {
cache.set('key_' + i, 'value_' + i);
}
return cache;
}
console.log('Object cache with 100 dynamic keys:');
const objCache = cacheWithObjectBad();
console.log(' Keys:', Object.keys(objCache).length);
console.log(' ā ļø Hidden class transitioned 100 times!');
console.log('\nMap cache with 100 dynamic keys:');
const mapCache = cacheWithMapGood();
console.log(' Size:', mapCache.size);
console.log(' ā
No hidden class issues - Maps handle this natively\n');
// Performance comparison
const CACHE_ITERS = 100000;
const objCacheTest = {};
const mapCacheTest = new Map();
// Warmup
for (let i = 0; i < 1000; i++) {
objCacheTest['warmup' + i] = i;
mapCacheTest.set('warmup' + i, i);
}
// Object set performance
const startObjSet = performance.now();
for (let i = 0; i < CACHE_ITERS; i++) {
objCacheTest['test' + (i % 100)] = i;
}
const objSetTime = performance.now() - startObjSet;
// Map set performance
const startMapSet = performance.now();
for (let i = 0; i < CACHE_ITERS; i++) {
mapCacheTest.set('test' + (i % 100), i);
}
const mapSetTime = performance.now() - startMapSet;
console.log('Dynamic key set performance:');
console.log(`Object: ${objSetTime.toFixed(2)}ms`);
console.log(`Map: ${mapSetTime.toFixed(2)}ms\n`);
/**
* ============================================================
* EXAMPLE 10: REAL-WORLD PATTERN: API RESPONSES
* ============================================================
*/
console.log('=== Example 10: Real-World API Response Handling ===\n');
// Simulated API responses with different structures
const apiResponses = [
{ id: 1, name: 'Product A', price: 100 },
{ id: 2, name: 'Product B', price: 200, discount: 10 },
{ id: 3, name: 'Product C', price: 150, discount: 5, featured: true },
{ id: 4, name: 'Product D', price: 300 },
{ id: 5, name: 'Product E', price: 250, featured: true },
];
console.log('ā Raw API responses - inconsistent shapes:');
apiResponses.forEach((r, i) => {
console.log(` Response ${i + 1}:`, Object.keys(r).join(', '));
});
// Normalize to consistent shape
function normalizeProduct(raw) {
return {
id: raw.id,
name: raw.name,
price: raw.price,
discount: raw.discount ?? 0,
featured: raw.featured ?? false,
};
}
const normalizedProducts = apiResponses.map(normalizeProduct);
console.log('\nā
Normalized products - consistent shapes:');
normalizedProducts.forEach((p, i) => {
console.log(` Product ${i + 1}:`, Object.keys(p).join(', '));
});
// Now all products share the same hidden class
function calculateFinalPrice(product) {
const discount = product.price * (product.discount / 100);
return product.price - discount;
}
console.log('\nFinal prices (monomorphic access):');
normalizedProducts.forEach((p) => {
console.log(` ${p.name}: $${calculateFinalPrice(p)}`);
});
console.log();
/**
* ============================================================
* EXAMPLE 11: CHECKING HIDDEN CLASS SHARING
* ============================================================
*/
console.log('=== Example 11: Verifying Hidden Class Sharing ===\n');
// Function to test if objects likely share hidden class
// (Not 100% accurate but useful for testing)
function likelySameShape(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let i = 0; i < keys1.length; i++) {
if (keys1[i] !== keys2[i]) return false; // Order matters!
}
return true;
}
const a1 = { x: 1, y: 2 };
const a2 = { x: 3, y: 4 };
const b1 = { y: 1, x: 2 }; // Different order
const c1 = { x: 1, y: 2, z: 3 }; // Different properties
console.log('Testing shape similarity:');
console.log(`a1 & a2 (same order): ${likelySameShape(a1, a2)}`);
console.log(`a1 & b1 (different order): ${likelySameShape(a1, b1)}`);
console.log(`a1 & c1 (different props): ${likelySameShape(a1, c1)}`);
console.log();
/**
* ============================================================
* EXAMPLE 12: ARRAY ELEMENT SHAPES
* ============================================================
*/
console.log('=== Example 12: Arrays of Objects ===\n');
// ā
GOOD: Homogeneous array
const goodArray = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
];
// ā BAD: Heterogeneous array
const badArray = [
{ x: 1, y: 1 },
{ x: 2, y: 2, z: 2 },
{ y: 3, x: 3 },
];
function sumX(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i].x;
}
return sum;
}
// Benchmark
const largeGood = Array.from({ length: 10000 }, (_, i) => ({ x: i, y: i }));
const largeBad = Array.from({ length: 10000 }, (_, i) => {
if (i % 3 === 0) return { x: i, y: i };
if (i % 3 === 1) return { x: i, y: i, z: i };
return { y: i, x: i };
});
// Warmup
sumX(largeGood);
sumX(largeBad);
const itersArr = 10000;
const startGood = performance.now();
for (let i = 0; i < itersArr; i++) {
sumX(largeGood);
}
const goodTime = performance.now() - startGood;
const startBad = performance.now();
for (let i = 0; i < itersArr; i++) {
sumX(largeBad);
}
const badTime = performance.now() - startBad;
console.log('Homogeneous array:');
console.log(` All elements same shape`);
console.log(` Time: ${goodTime.toFixed(2)}ms`);
console.log('\nHeterogeneous array:');
console.log(` Elements have 3 different shapes`);
console.log(` Time: ${badTime.toFixed(2)}ms`);
console.log(` ${(badTime / goodTime).toFixed(2)}x slower\n`);
console.log('=== Examples Complete ===\n');