javascript
exercises
exercises.js⚡javascript
/**
* ============================================================
* 22.2 HIDDEN CLASSES & INLINE CACHING - EXERCISES
* ============================================================
*/
/**
* EXERCISE 1: IDENTIFYING HIDDEN CLASS TRANSITIONS
*
* For each code snippet, count the number of hidden class
* transitions and explain each one.
*/
// Snippet A
const objA = {};
objA.x = 1;
objA.y = 2;
objA.z = 3;
// How many transitions? ___
// Explain each:
// 1.
// 2.
// 3.
// 4.
// Snippet B
const objB = { x: 1, y: 2, z: 3 };
// How many transitions? ___
// Explain:
// Snippet C
function Point(x, y) {
this.x = x;
this.y = y;
}
const p1 = new Point(1, 2);
const p2 = new Point(3, 4);
// How many unique hidden classes created? ___
// Explain:
/**
* EXERCISE 2: SHAPE COMPATIBILITY
*
* Determine which objects share the same hidden class.
* Group them together.
*/
const a = { name: 'Alice', age: 25 };
const b = { name: 'Bob', age: 30 };
const c = { age: 35, name: 'Carol' };
const d = { name: 'Dave', age: 40, city: 'NYC' };
const e = {};
e.name = 'Eve';
e.age = 28;
const f = { name: 'Frank', age: 45 };
// Group objects by shared hidden class:
// Group 1:
// Group 2:
// Group 3:
// Explanation:
/**
* EXERCISE 3: FIX THE SHAPE INCONSISTENCY
*
* Refactor this code to ensure all user objects have
* the same hidden class.
*/
// ❌ PROBLEMATIC CODE
function createUser_Broken(data) {
const user = {};
user.id = data.id;
if (data.name) {
user.name = data.name;
}
if (data.email) {
user.email = data.email;
}
if (data.age && data.age > 0) {
user.age = data.age;
}
if (data.premium) {
user.premium = true;
user.tier = data.tier || 'basic';
}
return user;
}
// YOUR SOLUTION:
function createUser_Fixed(data) {
// TODO: Refactor to always create same shape
}
// Test cases - all should have same shape
// const user1 = createUser_Fixed({ id: 1, name: 'Alice' });
// const user2 = createUser_Fixed({ id: 2, email: 'bob@test.com', age: 25 });
// const user3 = createUser_Fixed({ id: 3, premium: true, tier: 'gold' });
/**
* EXERCISE 4: INLINE CACHE STATE PREDICTION
*
* Predict the IC state after each call.
* States: UNINITIALIZED, MONOMORPHIC, POLYMORPHIC, MEGAMORPHIC
*/
function getColor(obj) {
return obj.color;
}
// Predict IC state after each call:
getColor({ color: 'red' });
// IC State: ___
getColor({ color: 'blue' });
// IC State: ___
getColor({ color: 'green', size: 10 });
// IC State: ___
getColor({ color: 'yellow', weight: 5 });
// IC State: ___
getColor({ color: 'purple', id: 1 });
// IC State: ___
getColor({ color: 'orange', category: 'A' });
// IC State: ___
/**
* EXERCISE 5: OPTIMIZE THE FUNCTION
*
* This function has poor IC behavior. Refactor it to
* maintain monomorphic ICs.
*/
// ❌ PROBLEMATIC - many shapes
function processItems_Slow(items) {
return items.map((item) => {
// This receives many different shapes!
if (item.type === 'A') {
return { result: item.valueA * 2 };
} else if (item.type === 'B') {
return { result: item.valueB * 3 };
} else {
return { result: item.valueC * 4 };
}
});
}
const testItems = [
{ type: 'A', valueA: 10 },
{ type: 'B', valueB: 20 },
{ type: 'C', valueC: 30 },
{ type: 'A', valueA: 40 },
];
// YOUR SOLUTION:
// Option 1: Normalize input shapes
function normalizeItem(item) {
// TODO: Create consistent shape
}
// Option 2: Type-specific processors
function processTypeA(item) {
// TODO
}
function processTypeB(item) {
// TODO
}
function processTypeC(item) {
// TODO
}
function processItems_Fast(items) {
// TODO: Use normalized shapes or type-specific functions
}
/**
* EXERCISE 6: DELETE VS NULL
*
* Refactor this code to avoid using the delete operator.
*/
// ❌ USES DELETE
function cleanupObject_Bad(obj) {
if (!obj.isActive) {
delete obj.sessionId;
delete obj.lastActivity;
}
if (obj.tempData) {
delete obj.tempData;
}
return obj;
}
// YOUR SOLUTION:
function cleanupObject_Good(obj) {
// TODO: Use null instead of delete
}
// Test
// const testObj = {
// id: 1,
// isActive: false,
// sessionId: 'abc123',
// lastActivity: new Date(),
// tempData: { x: 1 }
// };
// console.log(cleanupObject_Good(testObj));
/**
* EXERCISE 7: CLASS DESIGN FOR CONSISTENT SHAPES
*
* Design a class hierarchy that ensures all instances
* have consistent hidden classes within their type.
*/
// Base class
class Vehicle {
constructor(/* TODO: what parameters? */) {
// TODO: Initialize ALL common properties
}
}
class Car extends Vehicle {
constructor(/* TODO */) {
super(/* TODO */);
// TODO: Initialize ALL Car-specific properties
}
}
class Motorcycle extends Vehicle {
constructor(/* TODO */) {
super(/* TODO */);
// TODO: Initialize ALL Motorcycle-specific properties
}
}
// All Cars should share one hidden class
// All Motorcycles should share another hidden class
/**
* EXERCISE 8: BENCHMARK SHAPES
*
* Create a benchmark that demonstrates the performance
* difference between monomorphic and polymorphic access.
*/
function createBenchmark() {
// TODO: Create monomorphic test data (all same shape)
const monoData = [];
// TODO: Create polymorphic test data (multiple shapes)
const polyData = [];
// TODO: Create access function
function accessValue(obj) {
// TODO
}
// TODO: Run and time both benchmarks
return {
runMonomorphic(iterations) {
// TODO
},
runPolymorphic(iterations) {
// TODO
},
compare(iterations) {
// TODO: Return timing comparison
},
};
}
// const benchmark = createBenchmark();
// console.log(benchmark.compare(1000000));
/**
* EXERCISE 9: API RESPONSE NORMALIZATION
*
* Create a normalizer for these varying API responses
* to ensure consistent hidden classes.
*/
const responses = [
{
id: 1,
title: 'Post 1',
author: { name: 'Alice' },
likes: 10,
},
{
id: 2,
title: 'Post 2',
// author missing
likes: 5,
comments: 3,
},
{
id: 3,
title: 'Post 3',
author: { name: 'Bob', verified: true },
// likes missing
featured: true,
},
];
// Create normalized version:
function normalizePost(raw) {
// TODO: Return consistent shape
// Should have: id, title, author (object with name, verified),
// likes, comments, featured
}
// const normalizedPosts = responses.map(normalizePost);
// All should have same structure
/**
* EXERCISE 10: DYNAMIC PROPERTY PATTERNS
*
* Refactor this code that uses dynamic properties on objects
* to use Map instead.
*/
// ❌ Dynamic keys on object
class CacheWithObject {
constructor() {
this.data = {};
this.timestamps = {};
}
set(key, value) {
this.data[key] = value;
this.timestamps[key] = Date.now();
}
get(key) {
return this.data[key];
}
isExpired(key, maxAge) {
const timestamp = this.timestamps[key];
if (!timestamp) return true;
return Date.now() - timestamp > maxAge;
}
}
// YOUR SOLUTION using Map:
class CacheWithMap {
constructor() {
// TODO
}
set(key, value) {
// TODO
}
get(key) {
// TODO
}
isExpired(key, maxAge) {
// TODO
}
}
/**
* EXERCISE 11: IDENTIFY THE ANTI-PATTERN
*
* Find the hidden class anti-patterns in this code
* and explain why each is problematic.
*/
class UserManager {
createUser(name, options = {}) {
const user = {
name: name,
createdAt: new Date(),
};
// Anti-pattern 1?
if (options.email) {
user.email = options.email;
}
// Anti-pattern 2?
if (options.role === 'admin') {
user.isAdmin = true;
user.permissions = ['read', 'write', 'delete'];
}
return user;
}
deactivateUser(user) {
// Anti-pattern 3?
delete user.permissions;
delete user.isAdmin;
user.deactivatedAt = new Date();
}
updateProfile(user, updates) {
// Anti-pattern 4?
for (const key in updates) {
user[key] = updates[key];
}
}
}
// YOUR ANALYSIS:
// Anti-pattern 1:
// Why problematic:
// Fix:
// Anti-pattern 2:
// Why problematic:
// Fix:
// Anti-pattern 3:
// Why problematic:
// Fix:
// Anti-pattern 4:
// Why problematic:
// Fix:
/**
* EXERCISE 12: SHAPE-AWARE FACTORY
*
* Implement a factory that tracks how many different
* shapes it has created (for debugging purposes).
*/
function createShapeAwareFactory(baseShape) {
// TODO: Implement factory that:
// 1. Always creates objects with the base shape
// 2. Tracks if created objects maintain the shape
// 3. Warns if shape is violated
const factory = {
create(data) {
// TODO
},
getShapeKeys() {
// Return the keys that define the shape
},
validateShape(obj) {
// Return true if obj has the expected shape
},
getStats() {
// Return { created, valid, invalid }
},
};
return factory;
}
// Usage:
// const userFactory = createShapeAwareFactory({
// id: null,
// name: '',
// email: null,
// active: false
// });
// const user = userFactory.create({ id: 1, name: 'Test' });
// console.log(userFactory.validateShape(user)); // true
/**
* ============================================================
* SOLUTIONS
* ============================================================
*/
// Scroll down for solutions...
// SOLUTION 3: Fix Shape Inconsistency
function createUser_Fixed_Solution(data) {
return {
id: data.id ?? null,
name: data.name ?? null,
email: data.email ?? null,
age: data.age && data.age > 0 ? data.age : null,
premium: Boolean(data.premium),
tier: data.tier ?? 'basic',
};
}
// SOLUTION 5: Optimize Function
function normalizeItem_Solution(item) {
return {
type: item.type,
value: item.valueA ?? item.valueB ?? item.valueC ?? 0,
};
}
function processItems_Fast_Solution(items) {
// Normalize first
const normalized = items.map(normalizeItem_Solution);
// Process with consistent shape
return normalized.map((item) => {
const multiplier = item.type === 'A' ? 2 : item.type === 'B' ? 3 : 4;
return { result: item.value * multiplier };
});
}
// SOLUTION 6: Delete vs Null
function cleanupObject_Good_Solution(obj) {
if (!obj.isActive) {
obj.sessionId = null;
obj.lastActivity = null;
}
obj.tempData = null;
return obj;
}
// SOLUTION 7: Class Design
class VehicleSolution {
constructor(make, model, year, color) {
this.make = make;
this.model = model;
this.year = year;
this.color = color;
this.mileage = 0;
}
}
class CarSolution extends VehicleSolution {
constructor(make, model, year, color, doors = 4, trunkSize = 0) {
super(make, model, year, color);
this.doors = doors;
this.trunkSize = trunkSize;
this.isConvertible = false;
}
}
class MotorcycleSolution extends VehicleSolution {
constructor(make, model, year, color, engineCC = 0, hasSidecar = false) {
super(make, model, year, color);
this.engineCC = engineCC;
this.hasSidecar = hasSidecar;
this.helmetStorage = false;
}
}
// SOLUTION 9: API Response Normalization
function normalizePostSolution(raw) {
return {
id: raw.id,
title: raw.title ?? '',
author: {
name: raw.author?.name ?? 'Anonymous',
verified: raw.author?.verified ?? false,
},
likes: raw.likes ?? 0,
comments: raw.comments ?? 0,
featured: raw.featured ?? false,
};
}
// SOLUTION 10: Cache with Map
class CacheWithMapSolution {
constructor() {
this.entries = new Map();
}
set(key, value) {
this.entries.set(key, {
value,
timestamp: Date.now(),
});
}
get(key) {
const entry = this.entries.get(key);
return entry?.value;
}
isExpired(key, maxAge) {
const entry = this.entries.get(key);
if (!entry) return true;
return Date.now() - entry.timestamp > maxAge;
}
}
// SOLUTION 12: Shape-Aware Factory
function createShapeAwareFactorySolution(baseShape) {
const shapeKeys = Object.keys(baseShape);
let created = 0;
let valid = 0;
let invalid = 0;
return {
create(data) {
const obj = {};
for (const key of shapeKeys) {
obj[key] = data[key] ?? baseShape[key];
}
created++;
if (this.validateShape(obj)) {
valid++;
} else {
invalid++;
console.warn('Shape violation detected!');
}
return obj;
},
getShapeKeys() {
return [...shapeKeys];
},
validateShape(obj) {
const objKeys = Object.keys(obj);
if (objKeys.length !== shapeKeys.length) return false;
for (let i = 0; i < shapeKeys.length; i++) {
if (objKeys[i] !== shapeKeys[i]) return false;
}
return true;
},
getStats() {
return { created, valid, invalid };
},
};
}
console.log('Exercises loaded. Start solving!');