Every object has a hidden link to another object, its prototype. When you read obj.x and obj has no own property x, the engine looks at the prototype, then the prototype's prototype, until it reaches null. That is the whole mechanism behind methods, inheritance and classes: [].push is not on your array, it is on Array.prototype, one object shared by every array ever made.
javascriptEdit it. ⌘/Ctrl + Enter runs. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const animal = {
legs: 4,
describe() { return `${this.name} has ${this.legs} legs` },
}
const dog = Object.create(animal) // dog's prototype is animal
dog.name = "Rex"
console.log(dog.describe()) // describe is found on animal; this is still dog
console.log(Object.hasOwn(dog, "name"), Object.hasOwn(dog, "legs"), "legs" in dog)
console.log(Object.getPrototypeOf(dog) === animal)
// Shadowing: an own property hides the inherited one
dog.legs = 3
console.log(dog.legs, animal.legs)
// The chain for a plain array
const arr = [1, 2]
console.log(Object.getPrototypeOf(arr) === Array.prototype, Object.getPrototypeOf(Array.prototype) === Object.prototype, Object.getPrototypeOf(Object.prototype))
console.log(Object.hasOwn(arr, "push"), typeof arr.push)
// Constructor functions: how it was done before class (still what class does underneath)
function Account(owner) {
this.owner = owner
this.balance = 0
}
Account.prototype.deposit = function (a) { this.balance += a; return this }
const x = new Account("Ada"), y = new Account("Bob")
x.deposit(10)
console.log(x.balance, y.balance, x.deposit === y.deposit, x instanceof Account, x.constructor === Account)You should see
Rex has 4 legs
true false true
true
3 4
true true null
false function
10 0 true true true
VisualizeLooking up dog.describe()Step 1 / 5
1const animal = { legs: 4, describe() { return this.name + " " + this.legs } }
2const dog = Object.create(animal)
3dog.name = "Rex"
4console.log(dog.describe())
Line 2Create an empty object whose prototype link points at animal.
All 5 steps as a table
| Step | Line | What happened | Variables now |
|---|
| 1 | 2 | Create an empty object whose prototype link points at animal. | dog = {} → animal |
| 2 | 3 | Set an own property on dog. | dog = { name: "Rex" } → animal |
| 3 | 4 | Look up describe on dog. Not an own property. | |
| 4 | 4 | Follow the prototype link to animal. Found describe there. | |
| 5 | 4 | Call it with this = dog (rule 1: the object before the dot). Inside, this.name is own ("Rex"); this.legs is not own — walk to animal again → 4. | |
Why it matters even if you only write classesEvery "why is this wrong", every "method is not a function", every monkey-patch like Array.prototype.last = …, and every framework's magic is the prototype chain. Ten minutes here saves hours later.