Object Prototype and Prototypal Inheritance
In short: every object carries a hidden link, [[Prototype]], to
another object. Reading a property that isn't on the object itself doesn't
fail right away — JavaScript walks the [[Prototype]] chain, checking
each linked object in turn, until it finds a match or runs out of chain.
class and extends are not a separate system from this — they're
syntax built entirely on top of it. A class's methods live on its
.prototype object, and extends simply links one class's .prototype
to point at its parent's, continuing the same chain.
![A chain of three shelves: d (instance, own: name = "Rex", no bark() here) → [[Prototype]] → Dog.prototype (bark(), found here, stop looking) → [[Prototype]] → Animal.prototype (speak()) — d.bark(): not own, check Dog.prototype, found, run it; d.speak(): not own, not on Dog.prototype, keep climbing to Animal.prototype, found — class syntax builds exactly this chain](/api/assets/javascript/assets/8ab365d058b468eb.png)
Confirm a class's methods live on .prototype, not the instance
A class instance's own properties come from the constructor; its methods
are found one step up the [[Prototype]] chain, on the class's
.prototype object:
Object.getPrototypeOf(s) === Song.prototype is true — that's the
actual [[Prototype]] link. s.hasOwnProperty("describe") is false —
describe was never copied onto s itself. Song.prototype.hasOwnProperty("describe")
is true — that's where it actually lives. "describe" in s is still
true, because in checks the whole chain, not just own properties.
Walk a two-level chain built by extends
extends links one class's .prototype to its parent's — a lookup that
doesn't find something on the first shelf keeps climbing:
d's [[Prototype]] is Dog.prototype; Dog.prototype's own
[[Prototype]] is Animal.prototype — a genuine chain, two links long.
d.bark() finds bark on the first shelf, Dog.prototype. d.speak()
isn't there, so the lookup keeps climbing to Animal.prototype, where it
finds speak and runs it.
Build the same chain by hand, with Object.create
class/extends is convenient syntax for exactly this mechanism — the
same chain can be built directly with Object.create:
Object.create(base) makes a new object whose [[Prototype]] is base
directly — no class involved at all. child.greet() still works, found
by walking up to base. child.hasOwnProperty("greet") is false — same
result as the class example, because it's genuinely the same underlying
mechanism.
Quick practice
What does Object.getPrototypeOf(someInstance) === SomeClass.prototype tell you?
Why does d.hasOwnProperty("bark") return false even though d.bark() works?
In class Dog extends Animal, what does extends actually set up?
What does Object.create(base) do?
When a property lookup doesn't find a match on an object itself, what happens next?
🎯 Your turn
Given
class Animal { speak() { return "..."; } } class Dog extends Animal { bark() { return "Woof"; } } const d = new Dog();,
explain, in terms of [[Prototype]] and the lookup chain, exactly where
d.bark() and d.speak() are found, and why
d.hasOwnProperty("bark") is false even though d.bark() works fine.
Write your explanation and submit it — there's no code to run here, just your reasoning.
