javascript

exercises

exercises.js
/**
 * ============================================================
 * 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!');
Exercises - JavaScript Tutorial | DeepML