Lesson 9·M1 Core Mechanics
Prototypes & Inheritance
OpenLearning Objectives
By the end of this lesson you should be able to:
- Explain what happens when you read a property that doesn't exist on an object
- Distinguish
__proto__fromprototypewithout hesitating - Describe what
newactually does, in four steps - Say why
classis syntax over prototypes, not a different model - Explain why methods live on the prototype rather than the instance
Every Object Has a Prototype
An object has a hidden link to another object — its prototype. When you read a property that isn't there, the engine follows the link.
const animal = {
eats: true,
describe() {
return `eats: ${this.eats}`;
},
};
const rabbit = Object.create(animal);
rabbit.jumps = true;
console.log(rabbit.jumps);
console.log(rabbit.eats);
console.log(rabbit.describe());
Output:
true
true
eats: true
rabbit has one own property. eats and describe were found one link up — and note
this inside describe still refers to rabbit, because this comes from the call
(Lesson 10), not from where the method lives.
The Prototype Chain
The search continues until it finds the property or runs out of links:
rabbit { jumps: true }
│ __proto__
▼
animal { eats: true, describe() }
│ __proto__
▼
Object.prototype { toString, hasOwnProperty, … }
│ __proto__
▼
null ← end of the chain
console.log(Object.getPrototypeOf(rabbit) === animal);
console.log(Object.getPrototypeOf(animal) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));
console.log(rabbit.missing);
Output:
true
true
null
undefined
Reading walks the chain. Writing never does — assignment always creates an own property on the object itself:
rabbit.eats = false;
console.log(rabbit.eats, animal.eats);
Output:
false true
The prototype is untouched; rabbit now shadows it. That's the same shadowing idea from
Lesson 2.
__proto__ vs prototype
This is the confusion the question is really testing.
| What it is | Lives on | |
|---|---|---|
__proto__ | The actual link to this object's prototype | Every object |
prototype | The object that will become __proto__ for instances | Functions only |
function Dog(name) {
this.name = name;
}
Dog.prototype.speak = function () {
return `${this.name} barks`;
};
const rex = new Dog('Rex');
console.log(rex.speak());
console.log(Object.getPrototypeOf(rex) === Dog.prototype);
console.log(rex.prototype);
Output:
Rex barks
true
undefined
An instance has no prototype property — only functions do. Use Object.getPrototypeOf(obj)
rather than obj.__proto__, which is legacy and only standardised for web compatibility.
What new Actually Does
Four steps, worth being able to recite:
- Create a fresh empty object
- Set its prototype to the constructor's
.prototype - Call the constructor with
thisbound to that object - Return it — unless the constructor explicitly returns an object
function myNew(Ctor, ...args) {
const obj = Object.create(Ctor.prototype); // steps 1 and 2
const result = Ctor.apply(obj, args); // step 3
return typeof result === 'object' && result !== null ? result : obj; // step 4
}
const rex2 = myNew(Dog, 'Rex II');
console.log(rex2.speak(), rex2 instanceof Dog);
Output:
Rex II barks true
Writing myNew in an interview is a strong answer to "what does new do?".
Methods Live on the Prototype for a Reason
function Point(x, y) {
this.x = x;
this.y = y;
this.badDistance = function () { return Math.hypot(this.x, this.y); }; // ❌ per instance
}
Point.prototype.distance = function () { return Math.hypot(this.x, this.y); }; // ✅ shared
const p1 = new Point(3, 4);
const p2 = new Point(6, 8);
console.log(p1.distance(), p2.distance());
console.log(p1.distance === p2.distance);
console.log(p1.badDistance === p2.badDistance);
Output:
5 10
true
false
A prototype method is created once and shared by every instance. Defining it in the constructor creates a new function object per instance — the memory argument for prototypes, and a good thing to mention unprompted.
class Is Syntax Over This
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog2 extends Animal {
speak() {
return `${super.speak()} — a bark`;
}
}
const d = new Dog2('Rex');
console.log(d.speak());
console.log(Object.getPrototypeOf(d) === Dog2.prototype);
console.log(Object.getPrototypeOf(Dog2.prototype) === Animal.prototype);
console.log(typeof Dog2);
Output:
Rex makes a sound — a bark
true
true
function
typeof Dog2 is 'function' — a class is a constructor function with a prototype object.
The differences are ergonomic and a few safety rules: class bodies are always strict, methods
are non-enumerable, and calling a class without new throws.
Note
"Is JavaScript classical or prototypal?" — prototypal. class is sugar that makes the
prototype wiring readable; there are no classes in the Java sense underneath.
Checking Types and Ownership
console.log(d instanceof Dog2, d instanceof Animal);
console.log(Object.hasOwn(d, 'name'), Object.hasOwn(d, 'speak'));
console.log('speak' in d);
console.log(Object.keys(d));
Output:
true true
true false
true
[ 'name' ]
instanceof asks whether a constructor's prototype appears anywhere in the chain. name is
an own property; speak is inherited, so it's in the object but not hasOwn, and
Object.keys skips it.
Pitfall
Never add to Object.prototype. Every object in the program inherits it, so a stray key
shows up in every for...in loop and in libraries that never expected it. Extending
built-in prototypes at all is a code smell — reach for a plain function instead.
Common Interview Questions
Q1. What is prototypal inheritance?
Every object has a link to another object, its prototype. When you read a property the engine checks the object, then walks that chain until it finds it or reaches
null.It's the same shape as the scope chain — a lookup that goes one way — except it's for properties rather than variables.
Q2. What's the difference between __proto__ and prototype?
__proto__is the actual link on an object, pointing to its prototype.prototypeis a property that only functions have, holding the object that instances created withnewwill get as their__proto__.So
rex.__proto__ === Dog.prototype. In modern code I'd read it withObject.getPrototypeOf.
Q3. What does new do?
Four things: creates an empty object, sets its prototype to the constructor's
.prototype, calls the constructor withthisbound to that object, and returns it unless the constructor returns an object of its own.
Q4. Is class a different inheritance model?
No — it's syntax over prototypes.
typeof MyClassis'function', and its methods live onMyClass.prototype. The additions are guardrails: strict mode, non-enumerable methods, and a throw if you call it withoutnew.
Q5. Why put methods on the prototype instead of in the constructor?
Because a prototype method is created once and shared by every instance, while one defined in the constructor is a new function object per instance. With a thousand instances that's a thousand copies of the same function.
Q6. What happens when you assign to a property that exists on the prototype?
The assignment creates an own property on the instance, shadowing the prototype's. The prototype is never modified through an instance — reads walk the chain, writes don't.
Senior follow-up: How do instanceof and hasOwnProperty differ in what they inspect?
instanceofwalks the prototype chain looking for a constructor's.prototypeobject, so it answers "is this type anywhere in my ancestry?".Object.hasOwnlooks only at the object itself and ignores the chain entirely.That's why an inherited method is
inan object and passesinstanceoffor its class, but doesn't show up inObject.keysorhasOwn.
Best Practices
✅ Use class syntax for new code — same model, far more readable
✅ Read prototypes with Object.getPrototypeOf, not __proto__
✅ Use Object.hasOwn when you mean own properties only
✅ Prefer composition over deep inheritance chains
❌ Never extend Object.prototype or other built-ins
❌ Don't use Object.setPrototypeOf on existing objects — it deoptimises the engine badly
Coding Exercise
Predict every line, then run it.
const base = { greet() { return 'hi'; } };
const child = Object.create(base);
console.log(child.greet());
console.log(Object.hasOwn(child, 'greet'));
child.greet = () => 'hello';
console.log(child.greet(), base.greet());
function F() {}
const f = new F();
console.log(Object.getPrototypeOf(f) === F.prototype, f.prototype);
Answers
hi
false
hello hi
true undefined
Reads walk the chain; writes create an own property and shadow it; instances have no
prototype of their own.
Mini Challenge
Implement myNew(Ctor, ...args) from memory, then prove it works by using it with a
constructor whose prototype has a method — checking instanceof and that the method is
shared, not copied.
Then explain what your implementation does with a constructor that returns an object.
Lesson Summary
Recap
- Property lookup walks the prototype chain until it hits
null - Reads walk the chain; writes always create an own property
__proto__is an object's link;prototypeis a property of functionsnew= create, link, call withthis, returnclassis syntax over prototypes —typeofa class is'function'- Prototype methods are shared once; constructor-defined methods are copied per instance
instanceofsearches the chain;Object.hasOwnnever leaves the object
Check your understanding
Answer each one out loud. Then rate yourself honestly.
0/6Describe what happens when you read a property that isn't on the object.
State the difference between __proto__ and prototype in one sentence each.
Recite the four steps of new.
Why doesn't assigning to an inherited property change the prototype?
Why are methods put on the prototype rather than created in the constructor?
In what sense is class "just syntax"? Name two things it does add.
What's Next
Lesson 10 — this and Binding. Four rules that decide what this is, and knowing all
four calmly is one of the strongest senior signals in a JavaScript interview.