Advanced JavaScript

Master advanced JavaScript concepts including design patterns, performance optimization, and modern development practices.

advanced JavaScript 7 hours

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!"