Back to Notes

JS Prototype & OOP

The Prototype Chain

Every object has an internal [[Prototype]] link to another object. Accessing a missing property walks up this chain until found or null is reached.

const animal = { breathes: true };
const dog = Object.create(animal);   // dog.__proto__ === animal
dog.name = "Rex";

dog.name       // "Rex" — own property
dog.breathes   // true  — found on the prototype
dog.toString   // function — found further up, on Object.prototype

dog.breathes = false;   // WRITES always create an OWN property — never mutates the prototype
animal.breathes;        // still true — untouched

Prototypal vs. Classical Inheritance

Classical (Java/C++): classes are blueprints, objects are instances, inheritance via extends. Prototypal (JS): objects inherit directly from other objects via the prototype chain — class syntax is syntactic sugar over this same underlying mechanism, not a genuinely different inheritance model.

class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
  speak() { return `${this.name} barks`; }
}

const d = new Dog("Rex");
d.speak();                                    // "Rex barks"
d instanceof Dog && d instanceof Animal;      // true, true
Object.getPrototypeOf(d) === Dog.prototype;   // true — confirms class syntax IS the prototype chain

Objects Are Reference Types

const a = { x: 1 };
const b = a;         // b is the SAME object, not a copy
b.x = 2;
a.x;                 // 2 — mutating through b affects a

This is the JS instance of the general reference-semantics discussion in [[Identity, Mutability & Memory]] (Python) — the underlying concept (mutable reference types vs. copied primitives) recurs across languages, only the specific syntax differs.

Function Declaration vs. Expression

function add(a, b) { return a + b; }       // declaration — fully hoisted, callable before this line
const add = function(a, b) { return a + b; };  // expression — NOT hoisted as a callable function
const add = (a, b) => a + b;                    // arrow expression — also not hoisted, no own `this`

localStorage / sessionStorage / Cookies

localStoragesessionStorageCookies
Capacity~5MB~5MB~4KB
ExpiryNever (until cleared)Tab/session closeConfigurable
Sent to serverNoNoYes, on every request
ScopeOriginOrigin + tabDomain + path

Only cookies are automatically sent with every HTTP request — this is precisely why session tokens historically lived in cookies (server needs to see them without extra client-side wiring) and why localStorage is the wrong place for anything the server needs on every request without an explicit header.

Interview Drills

  1. What does Object.getPrototypeOf(instance) === MyClass.prototype confirm about JS class syntax? — That class is genuinely implemented as prototypal inheritance underneath — an instance's prototype IS the class's .prototype object, the same mechanism as manually chaining objects with Object.create(), not a separate classical-inheritance system bolted on top.
  2. Why does dog.breathes = false not affect the animal object it inherits breathes from? — Writing a property always creates (or updates) an own property directly on the target object — the prototype chain is only consulted on reads for properties the object doesn't have itself; a write never walks up the chain to mutate an inherited property's source.
  3. Why would you choose a cookie over localStorage for an auth session token in a traditional server-rendered app? — Cookies are automatically included in every HTTP request to the matching domain, which is exactly what a server needs to authenticate incoming requests without any additional client-side code to attach a token manually — localStorage requires the client to explicitly read and attach the value to each request itself.

Cross-links

[[JS Core Language & Data Types]] · [[JS Closures & Async]] · [[Python OOP & Data Model]] · [[SOLID Principles]]