← JavaScript core concepts for interviews

Lesson 3 of 5

Objects, prototypes and classes

2 min read

Prototypal inheritance

Every object has a hidden link ([[Prototype]]) to another object. When you read a property that isn't on the object, JavaScript looks up the prototype chain until it finds it or reaches null.

js
const animal = { speak() { return `${this.name} makes a sound`; } };
const dog = Object.create(animal);
dog.name = "Rex";
dog.speak(); // found on animal via the chain

Arrays get map, filter and friends from Array.prototype; that's why every array has them.

Classes are syntax over prototypes

js
class Account {
  #balance = 0;                   // truly private field
  constructor(owner) { this.owner = owner; }
  deposit(amount) {
    if (amount <= 0) throw new Error("Amount must be positive");
    this.#balance += amount;
    return this;                  // allows chaining
  }
  get balance() { return this.#balance; }
  static compare(a, b) { return a.balance - b.balance; }
}

class SavingsAccount extends Account {
  constructor(owner, rate) { super(owner); this.rate = rate; }
  addInterest() { return this.deposit(this.balance * this.rate); }
}

Methods live on Account.prototype, shared by all instances, not copied into each object.

Copying objects

js
const copy = { ...original };            // shallow: nested objects are still shared
const deep = structuredClone(original);  // deep copy (modern browsers and Node 17+)

JSON.parse(JSON.stringify(x)) also deep-copies but loses dates, undefined, functions, Maps and Sets.

Useful built-ins

  • Object.keys / values / entries, Object.fromEntries
  • Object.freeze (shallow, makes properties read-only)
  • Map (any key type, keeps insertion order, has .size) vs plain object (string/symbol keys)
  • Set for unique values: [...new Set(arr)] removes duplicates
  • WeakMap for metadata about objects without preventing garbage collection

Array methods you must know cold

js
const orders = [{ city: "Pune", total: 500 }, { city: "Delhi", total: 300 }, { city: "Pune", total: 200 }];

orders.filter((o) => o.total > 250);                  // keep some
orders.map((o) => o.total);                            // transform each
orders.reduce((sum, o) => sum + o.total, 0);           // 1000: fold into one value
orders.find((o) => o.city === "Delhi");                // first match or undefined
orders.some((o) => o.total > 400);                     // true
orders.every((o) => o.total > 100);                    // true
[...orders].sort((a, b) => b.total - a.total);         // sort copies; sort() mutates!

// group by city
orders.reduce((acc, o) => { (acc[o.city] ??= []).push(o); return acc; }, {});

Know which mutate (push, pop, splice, sort, reverse) and which return new arrays (map, filter, slice, concat, toSorted).

Check yourself

  • What is the prototype chain?
  • What's the difference between a shallow and a deep copy?
  • Implement groupBy with reduce.

Sign up free to save your progress.