JS::OOP_Mastery
Overview / Session 03
Session 03 of 10  ·  2 hrs + 15 min review

Prototypes — What class Really Is

⏱ 2-hour session 📋 15-min review 🟡 Builds on Session 02 🗂 4 topics

1 — Why This Session Exists

You can write a lot of useful JavaScript OOP without ever thinking about prototypes — Sessions 01 and 02 already got you a working mental model. So why stop and look underneath the hood now? Two reasons. First, class is syntax sugar — it compiles down to prototype operations that existed in JS for two decades before class did. Second, you will read code — old codebases, libraries, interview questions — that uses prototypes directly, with no class keyword in sight. If you only know the sugar, that code looks like a different language.

Every object in JavaScript has an internal link to another object: its prototype. When you access a property or method that isn't found directly on the object, JavaScript doesn't give up — it walks up to the prototype, then that prototype's prototype, and so on. This is the prototype chain.

2 — Object.create() and the Prototype Chain

Before class existed, this is how people built prototype-based "inheritance" by hand — and it's still the clearest way to see the mechanism with nothing hidden.

JavaScript — manual prototype linking
const animalProto = {
    describe() {
        return `${this.name} makes a sound`;
    }
};

// dog's prototype is set to animalProto — they're now linked
const dog = Object.create(animalProto);
dog.name = 'Rex';

console.log(dog.describe()); // "Rex makes a sound"
// dog doesn't OWN describe() — JS looked it up on animalProto

console.log(dog.hasOwnProperty('name'));     // true  — own property
console.log(dog.hasOwnProperty('describe')); // false — inherited via the chain

The chain keeps going until it hits null. Every plain object you've ever made ultimately chains up to Object.prototype, which is where common methods like toString() and hasOwnProperty() actually live — you never defined them, but every object can use them because of this chain.

3 — prototype vs __proto__

These two terms look almost identical and that's exactly why they confuse people. They are not the same thing, and mixing them up is one of the most common JS prototype bugs.

  • .prototype — a property that exists on functions and classes. It's the object that will become the prototype of any instance created with new.
  • .__proto__ — a property that exists on instances. It's the actual link to that instance's prototype — i.e. it points at the .prototype object of whatever created it.
JavaScript — telling them apart
class Dog {
    constructor(name) { this.name = name; }
    bark() { return `${this.name}: Woof!`; }
}

const rex = new Dog('Rex');

// Dog.prototype — the shared blueprint object, holds bark()
console.log(typeof Dog.prototype);        // "object"
console.log(Dog.prototype.bark);       // the bark function itself

// rex.__proto__ — rex's actual link, points AT Dog.prototype
console.log(rex.__proto__ === Dog.prototype); // true

// Prefer this over __proto__ in real code — same idea, standard API
console.log(Object.getPrototypeOf(rex) === Dog.prototype); // true

Rule of thumb: .prototype is something you look at on the class itself when designing it. __proto__ (or better, Object.getPrototypeOf()) is something you use on an instance when inspecting it. You'll rarely write __proto__ in production code — it's shown here so you recognize it when you see it.

4 — How class Desugars

Here is the core reveal of this session: the Dog class above and the hand-written version below behave identically. class is a cleaner way to write exactly this pattern.

JavaScript — class syntax
class Dog {
    constructor(name) { this.name = name; }
    bark() { return `${this.name}: Woof!`; }
}
JavaScript — the equivalent, pre-ES6 way
function Dog(name) {
    this.name = name;
}

// Methods go on the shared .prototype object, NOT inside the function
Dog.prototype.bark = function() {
    return `${this.name}: Woof!`;
};

const rex = new Dog('Rex');
console.log(rex.bark()); // "Rex: Woof!" — identical result

This is exactly why Session 02 told you instance methods are shared on the prototype rather than recreated per object: class attaches every method you write to Dog.prototype automatically, the same way the manual version does it with an explicit assignment. The constructor function and the new keyword work identically in both versions — only the spelling changed.

hasOwnProperty vs inherited — back to where we started

This ties Topics 2–4 together in one example:

JavaScript — own vs inherited, the class version
const rex = new Dog('Rex');

console.log(rex.hasOwnProperty('name')); // true  — set in the constructor, lives on rex itself
console.log(rex.hasOwnProperty('bark')); // false — found via the chain on Dog.prototype
console.log('bark' in rex);             // true  — `in` checks the whole chain, own + inherited

15-Minute Review — Session 03

Stop the session timer and switch to the review timer in the sidebar. Answer these questions from memory — then check.

Q1 — What happens when you access a property that isn't found directly on an object?

Q2 — Where do instance methods written inside a class body actually get stored?

Q3 — What's the difference between .prototype and .__proto__?

Q4 — For an instance rex created with new Dog('Rex'), which call correctly checks if name is rex's own property (not inherited)?

✓ Key concepts checklist

Next: Encapsulation — Private Fields & Accessors →