JavaScript Object Prototypes & inheritance
Master JavaScript's prototype-based inheritance system and understand how objects inherit properties and methods. This is a foundational concept in programming and web interactivity that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world JavaScript experience. Take your time with each section and practice the examples
Prototype Fundamentals
JavaScript uses prototype-based inheritance, where objects can inherit properties and methods from other objects through a prototype chain.. This is an essential concept that every JavaScript developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Prototype Chain
// Prototype chain demonstration
const person = {
name: "John",
age: 30,
greet() {
return `Hello, my name is ${this.name}`;
}
};
const student = Object.create(person);
student.study = function() {
return "I'm studying JavaScript";
};
console.log(student.name); // "John" (inherited)
console.log(student.greet()); // "Hello, my name is John" (inherited)
console.log(student.study()); // "I'm studying JavaScript" (own method)
// Prototype chain visualization
console.log(student.__proto__ === person); // true
console.log(person.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null (end of chain)
// Constructor functions and prototypes
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return `Hello, my name is ${this.name}`;
};
Person.prototype.getAge = function() {
return this.age;
};
const john = new Person("John", 30);
const jane = new Person("Jane", 25);
console.log(john.greet()); // "Hello, my name is John"
console.log(jane.getAge()); // 25
// Prototype inheritance
function Student(name, age, grade) {
Person.call(this, name, age); // Call parent constructor
this.grade = grade;
}
// Set up prototype chain
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.study = function() {
return "I'm studying hard!";
};
const alice = new Student("Alice", 20, "A");
console.log(alice.greet()); // "Hello, my name is Alice"
console.log(alice.study()); // "I'm studying hard!"
console.log(alice.getAge()); // 20Modern inheritance with Classes
// ES6+ Class inheritance
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
}
speak() {
return `${this.name} makes a sound`;
}
getInfo() {
return `${this.name} is a ${this.species}`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name, "dog"); // Call parent constructor
this.breed = breed;
}
speak() {
return `${this.name} barks: Woof!`;
}
fetch() {
return `${this.name} fetches the ball`;
}
}
class Cat extends Animal {
constructor(name, color) {
super(name, "cat");
this.color = color;
}
speak() {
return `${this.name} meows: Meow!`;
}
purr() {
return `${this.name} purrs contentedly`;
}
}
// Usage
const dog = new Dog("Buddy", "Golden Retriever");
const cat = new Cat("Whiskers", "Orange");
console.log(dog.speak()); // "Buddy barks: Woof!"
console.log(cat.speak()); // "Whiskers meows: Meow!"
console.log(dog.fetch()); // "Buddy fetches the ball"
console.log(cat.purr()); // "Whiskers purrs contentedly"
// Method overriding and super
class Vehicle {
constructor(make, model) {
this.make = make;
this.model = model;
}
start() {
return `${this.make} ${this.model} is starting`;
}
getInfo() {
return `${this.make} ${this.model}`;
}
}
class ElectricCar extends Vehicle {
constructor(make, model, batteryCapacity) {
super(make, model);
this.batteryCapacity = batteryCapacity;
}
start() {
return super.start() + " silently"; // Call parent method
}
getInfo() {
return super.getInfo() + ` with ${this.batteryCapacity}kWh battery`;
}
}
const tesla = new ElectricCar("Tesla", "Model 3", 75);
console.log(tesla.start()); // "Tesla Model 3 is starting silently"
console.log(tesla.getInfo()); // "Tesla Model 3 with 75kWh battery"Practice Exercise: inheritance
// Exercise: Build a Shape Hierarchy
class Shape {
constructor(color) {
this.color = color;
}
getColor() {
return this.color;
}
getArea() {
throw new Error("getArea() must be implemented by subclass");
}
getPerimeter() {
throw new Error("getPerimeter() must be implemented by subclass");
}
getInfo() {
return `A ${this.color} ${this.constructor.name}`;
}
}
class Circle extends Shape {
constructor(color, radius) {
super(color);
this.radius = radius;
}
getArea() {
return Math.PI * this.radius ** 2;
}
getPerimeter() {
return 2 * Math.PI * this.radius;
}
getInfo() {
return super.getInfo() + ` with radius ${this.radius}`;
}
}
class Rectangle extends Shape {
constructor(color, width, height) {
super(color);
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
getPerimeter() {
return 2 * (this.width + this.height);
}
getInfo() {
return super.getInfo() + ` with width ${this.width} and height ${this.height}`;
}
}
class Square extends Rectangle {
constructor(color, side) {
super(color, side, side);
this.side = side;
}
getInfo() {
return super.getInfo().replace("Rectangle", "Square").replace(`width ${this.side} and height ${this.side}`, `side ${this.side}`);
}
}
// Test the shape hierarchy
const shapes = [
new Circle("red", 5),
new Rectangle("blue", 4, 6),
new Square("green", 3)
];
shapes.forEach(shape => {
console.log(shape.getInfo());
console.log(`Area: ${shape.getArea().toFixed(2)}`);
console.log(`Perimeter: ${shape.getPerimeter().toFixed(2)}`);
console.log("---");
});
// Challenge: Add more shapes (Triangle, Ellipse, etc.)
// and implement a shape calculator that can work with any shape type