Chapter 4: Prototypes and Inheritance
Chapter 4 of 15
Chapter 4: Prototypes and Inheritance
4.1 Prototype Chain
JavaScript uses prototype-based inheritance, where objects can inherit properties and methods from other objects through the prototype chain. This is fundamentally different from class-based inheritance.
Understanding Prototypes:
// Every object has a prototype (except Object.prototype)
const obj = {};
console.log(obj.__proto__); // Points to Object.prototype
// Prototype chain lookup
const person = {
name: 'John',
greet: function() {
return `Hello, ${this.name}`;
}
};
const student = Object.create(person);
student.study = function() {
return `${this.name} is studying`;
};
console.log(student.name); // "John" - from prototype
console.log(student.greet()); // "Hello, John" - from prototype
console.log(student.study()); // "John is studying" - own method
Prototype Chain Traversal:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return `${this.name} makes a sound`;
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// Set up prototype chain
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
return `${this.name} barks!`;
};
const myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.bark()); // "Buddy barks!" - from Dog.prototype
console.log(myDog.speak()); // "Buddy makes a sound" - from Animal.prototype
console.log(myDog.toString()); // "[object Object]" - from Object.prototype
4.2 ES6 Classes vs Prototypes
ES6 classes are syntactic sugar over JavaScript's prototype-based inheritance. Under the hood, they still use prototypes.
Class Syntax:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
static getSpecies() {
return 'Animal';
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
return `${this.name} barks!`;
}
speak() {
return `${super.speak()} and barks!`;
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.bark()); // "Buddy barks!"
console.log(myDog.speak()); // "Buddy makes a sound and barks!"
console.log(Dog.getSpecies()); // "Animal" - static method
Prototype Equivalent:
// Same functionality using prototypes
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return `${this.name} makes a sound`;
};
Animal.getSpecies = function() {
return 'Animal';
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
return `${this.name} barks!`;
};
Dog.prototype.speak = function() {
return `${Animal.prototype.speak.call(this)} and barks!`;
};
Dog.getSpecies = Animal.getSpecies;
4.3 Prototype Methods and Properties
Adding Methods to Prototype:
Array.prototype.last = function() {
return this[this.length - 1];
};
const arr = [1, 2, 3, 4, 5];
console.log(arr.last()); // 5
// Note: Modifying built-in prototypes is generally not recommended
Checking Prototype:
const obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
console.log(obj instanceof Object); // true
function Person() {}
const person = new Person();
console.log(person instanceof Person); // true
console.log(Object.getPrototypeOf(person) === Person.prototype); // true
4.4 Object.create() and Inheritance
const animal = {
init: function(name) {
this.name = name;
return this;
},
speak: function() {
return `${this.name} makes a sound`;
}
};
const dog = Object.create(animal);
dog.bark = function() {
return `${this.name} barks!`;
};
const myDog = Object.create(dog).init('Buddy');
console.log(myDog.speak()); // "Buddy makes a sound"
console.log(myDog.bark()); // "Buddy barks!"