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.
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.
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.
class Dog { constructor(name) { this.name = name; } bark() { return `${this.name}: Woof!`; } }
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:
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)?