Repository navigation
Expand file tree
/
Copy path01-prototypes.js
More file actions
89 lines (75 loc) · 3.39 KB
/
Copy path01-prototypes.js
File metadata and controls
89 lines (75 loc) · 3.39 KB
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* 01 — PROTOTYPES AND THE PROTOTYPE CHAIN
* ----------------------------------------
* Every JS object has an internal link ([[Prototype]]) to another object.
* When you access a property, the engine looks on the object itself first,
* then walks up the chain of prototypes until it finds the property or
* hits `null`. This is how inheritance works in JS — there are no
* "classes" at the engine level, just objects linked to other objects.
*/
// ── 1. Constructor functions and `.prototype` ──────────────────────────
function Animal(name) {
this.name = name;
}
// Methods go on the prototype, not on each instance, so every Animal
// instance SHARES one copy of `speak` instead of getting its own.
Animal.prototype.speak = function () {
return `${this.name} makes a sound.`;
};
const dog = new Animal("Rex");
console.log(dog.speak()); // "Rex makes a sound."
// `dog` itself has no `speak` property — it's found by walking the chain:
console.log(dog.hasOwnProperty("speak")); // false
console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
// ── 2. Prototypal inheritance between constructors ──────────────────────
function Dog(name, breed) {
Animal.call(this, name); // borrow Animal's constructor logic
this.breed = breed;
}
// Link Dog.prototype -> Animal.prototype so Dog instances can reach
// Animal's methods through the chain.
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // restore constructor reference
Dog.prototype.bark = function () {
return `${this.name} barks!`;
};
const rex = new Dog("Rex", "Labrador");
console.log(rex.speak()); // inherited from Animal.prototype
console.log(rex.bark()); // own prototype method
// The chain: rex -> Dog.prototype -> Animal.prototype -> Object.prototype -> null
console.log(Object.getPrototypeOf(Object.getPrototypeOf(rex)) === Animal.prototype); // true
// ── 3. `class` syntax is sugar over the same mechanism ──────────────────
class Vehicle {
constructor(wheels) {
this.wheels = wheels;
}
describe() {
return `A vehicle with ${this.wheels} wheels.`;
}
}
class Car extends Vehicle {
constructor(brand) {
super(4); // calls Vehicle's constructor
this.brand = brand;
}
}
const car = new Car("Toyota");
console.log(car.describe()); // inherited, proves `extends` just wires up the prototype chain
console.log(Object.getPrototypeOf(Car.prototype) === Vehicle.prototype); // true
// ── 4. `__proto__` vs `.prototype` — a common source of confusion ───────
// `.prototype` exists on FUNCTIONS — it's the object that becomes the
// [[Prototype]] of instances created with `new`.
// `__proto__` exists on OBJECTS (and functions, since functions are
// objects too) — it's the actual accessor to that internal link.
console.log(rex.__proto__ === Dog.prototype); // true
console.log(typeof Dog.prototype); // 'object'
console.log(typeof Dog.__proto__); // 'function' (Dog inherits from Function.prototype)
// ── 5. Object.create — building the chain directly, no constructors ─────
const base = {
greet() {
return "hello from base";
},
};
const child = Object.create(base);
console.log(child.greet()); // walks up to `base`
console.log(Object.getPrototypeOf(child) === base); // true