Skip to main content

Command Palette

Search for a command to run...

Understanding Prototypes & Inheritance in JavaScript: The Blueprint Analogy

Updated
7 min read

Ever wondered how JavaScript objects share behaviors and properties? Let me take you on a journey through one of JavaScript's most powerful yet misunderstood features: prototypes and inheritance.

The Blueprint Story: Understanding Prototypes

Imagine you're an architect designing houses. You don't build each house from scratch—you create a blueprint that contains all the common features: doors, windows, plumbing systems. Each house built from this blueprint shares these features, but can also have its own unique characteristics.

This is exactly how JavaScript prototypes work!

// The "Blueprint" - Constructor Function
function House(address, color) {
    this.address = address;
    this.color = color;
}

// Shared features (methods) on the prototype
House.prototype.openDoor = function() {
    console.log(`Opening door at ${this.address}`);
};

House.prototype.paintWall = function(newColor) {
    this.color = newColor;
    console.log(`House painted ${newColor}`);
};

// Creating houses from the blueprint
const house1 = new House("123 Main St", "blue");
const house2 = new House("456 Oak Ave", "red");

house1.openDoor(); // "Opening door at 123 Main St"
house2.openDoor(); // "Opening door at 456 Oak Ave"

What Happens When Two Objects Are Created from One Class?

When you create house1 and house2 from the same constructor:

  1. Each object gets its own property storage - house1.address and house2.address are separate

  2. They share the same prototype methods - Both point to House.prototype.openDoor (same memory location!)

  3. Changes to the prototype affect all instances

console.log(house1.openDoor === house2.openDoor); // true (same function!)

// Adding a new method to the prototype
House.prototype.lockDoor = function() {
    console.log(`Locking door at ${this.address}`);
};

// Both houses can now use it!
house1.lockDoor(); // Works!
house2.lockDoor(); // Works!

The Prototype Chain: The Family Tree

Think of prototypes like a family tree of genetic traits. Your traits come from your parents, their traits come from grandparents, and so on.

house1 → House.prototype → Object.prototype → null
// Demonstration of the prototype chain
console.log(house1.toString()); // Inherited from Object.prototype

// The chain lookup process
house1.openDoor();
// 1. Does house1 have openDoor? No
// 2. Does House.prototype have openDoor? Yes! Execute it.

prototype vs __proto__: The Confusion Cleared

This is where many developers get confused. Let's break it down with our blueprint analogy:

  • prototype: The blueprint itself (only on constructor functions)

  • __proto__: The reference each house keeps to its blueprint

function Animal(name) {
    this.name = name;
}

Animal.prototype.speak = function() {
    console.log(`${this.name} makes a sound`);
};

const dog = new Animal("Rex");

// PROTOTYPE - the blueprint (only on constructor)
console.log(typeof Animal.prototype); // "object"
console.log(Animal.prototype.speak); // function

// __PROTO__ - the reference to the blueprint (on instances)
console.log(dog.__proto__ === Animal.prototype); // true
console.log(dog.prototype); // undefined (instances don't have prototype)

Visual Comparison: prototype vs __proto__

What Does new Really Do?

The new keyword is like a house-building robot. Let's see what it does step by step:

function Car(brand, model) {
    this.brand = brand;
    this.model = model;
}

Car.prototype.drive = function() {
    console.log(`${this.brand} ${this.model} is driving`);
};

const myCar = new Car("Toyota", "Camry");

When you use new Car("Toyota", "Camry"), JavaScript does this:

// What 'new' does behind the scenes:
function simulateNew(Constructor, ...args) {
    // 1. Create a new empty object
    const obj = {};

    // 2. Set the object's __proto__ to Constructor.prototype
    obj.__proto__ = Constructor.prototype;
    // or: Object.setPrototypeOf(obj, Constructor.prototype);

    // 3. Execute the constructor with 'this' bound to the new object
    const result = Constructor.apply(obj, args);

    // 4. Return the object (unless constructor returns its own object)
    return result instanceof Object ? result : obj;
}

const myCar2 = simulateNew(Car, "Honda", "Civic");
myCar2.drive(); // "Honda Civic is driving"

Inheritance Without extends: The Old-School Way

Before ES6 classes, JavaScript developers achieved inheritance using prototypes directly. It's like creating a specialized blueprint that builds upon a general one.

Method 1: Using Object.create()

// Parent "class"
function Vehicle(type) {
    this.type = type;
    this.speed = 0;
}

Vehicle.prototype.accelerate = function(amount) {
    this.speed += amount;
    console.log(`${this.type} accelerating to ${this.speed} mph`);
};

// Child "class"
function Motorcycle(type, brand) {
    // Call parent constructor
    Vehicle.call(this, type);
    this.brand = brand;
}

// Set up inheritance - Create prototype chain
Motorcycle.prototype = Object.create(Vehicle.prototype);
Motorcycle.prototype.constructor = Motorcycle;

// Add child-specific methods
Motorcycle.prototype.wheelie = function() {
    console.log(`${this.brand} ${this.type} doing a wheelie!`);
};

const myBike = new Motorcycle("Motorcycle", "Harley");
myBike.accelerate(50); // "Motorcycle accelerating to 50 mph"
myBike.wheelie(); // "Harley Motorcycle doing a wheelie!"

Method 2: Manual Prototype Assignment

function Animal(name) {
    this.name = name;
}

Animal.prototype.eat = function() {
    console.log(`${this.name} is eating`);
};

function Dog(name, breed) {
    Animal.call(this, name);
    this.breed = breed;
}

// Create the inheritance link
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    console.log(`${this.name} the ${this.breed} says Woof!`);
};

const buddy = new Dog("Buddy", "Golden Retriever");
buddy.eat();  // "Buddy is eating" (inherited)
buddy.bark(); // "Buddy the Golden Retriever says Woof!"

The Prototype Chain Visualization

Events in JavaScript: The Party Invitation Analogy

JavaScript events are like sending party invitations through an apartment building. Let's understand this with a real-world analogy.

The Apartment Building Model

Imagine an apartment building with floors and apartments:

  • Building = <html>

  • Floor = <body> or <div>

  • Apartment = <button> or <input>

When you knock on an apartment door (click a button), three things happen:

  1. Capturing Phase: The sound travels DOWN from the building entrance to your apartment

  2. Target Phase: You answer the door

  3. Bubbling Phase: The sound travels UP back through the building

Event Propagation Code Example

// HTML structure (conceptually):
// <div id="building">
//   <div id="floor">
//     <button id="apartment">Click Me</button>
//   </div>
// </div>

const building = document.getElementById('building');
const floor = document.getElementById('floor');
const apartment = document.getElementById('apartment');

// CAPTURING PHASE (going down) - set third parameter to true
building.addEventListener('click', function() {
    console.log('1. Building heard the knock (capturing)');
}, true);

floor.addEventListener('click', function() {
    console.log('2. Floor heard the knock (capturing)');
}, true);

// TARGET PHASE
apartment.addEventListener('click', function(event) {
    console.log('3. Apartment answers! (target)');
    // event.stopPropagation(); // Stop the sound from traveling
});

// BUBBLING PHASE (going up) - default behavior
floor.addEventListener('click', function() {
    console.log('4. Floor heard the answer (bubbling)');
});

building.addEventListener('click', function() {
    console.log('5. Building heard the answer (bubbling)');
});

// When you click the button, output:
// 1. Building heard the knock (capturing)
// 2. Floor heard the knock (capturing)
// 3. Apartment answers! (target)
// 4. Floor heard the answer (bubbling)
// 5. Building heard the answer (bubbling)

Event Delegation: The Receptionist Pattern

Instead of hiring a receptionist for each apartment, hire ONE receptionist at the building entrance who handles all deliveries.

// BAD: Adding listener to each button (like hiring many receptionists)
document.querySelectorAll('.apartment').forEach(button => {
    button.addEventListener('click', handleClick);
});

// GOOD: Event delegation (one receptionist for the building)
document.getElementById('building').addEventListener('click', function(event) {
    if (event.target.classList.contains('apartment')) {
        console.log(`Package delivered to apartment ${event.target.id}`);
    }
});

Common Event Methods

element.addEventListener('click', function(event) {
    // Stop bubbling up
    event.stopPropagation();

    // Prevent default behavior (like form submission)
    event.preventDefault();

    // Who was clicked?
    console.log(event.target);

    // Who is listening?
    console.log(event.currentTarget);

    // Where in the journey?
    console.log(event.eventPhase); // 1=capturing, 2=target, 3=bubbling
});

Practical Example: Building a Shape Hierarchy

Let's combine everything we've learned into a practical example:

// Base Shape "class"
function Shape(color) {
    this.color = color;
}

Shape.prototype.describe = function() {
    console.log(`A ${this.color} shape`);
};

// Circle inherits from Shape
function Circle(color, radius) {
    Shape.call(this, color); // Inherit properties
    this.radius = radius;
}

Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;

Circle.prototype.area = function() {
    return Math.PI * this.radius ** 2;
};

Circle.prototype.describe = function() {
    console.log(`A ${this.color} circle with radius ${this.radius}`);
};

// Rectangle inherits from Shape
function Rectangle(color, width, height) {
    Shape.call(this, color);
    this.width = width;
    this.height = height;
}

Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

Rectangle.prototype.area = function() {
    return this.width * this.height;
};

Rectangle.prototype.describe = function() {
    console.log(`A ${this.color} rectangle ${this.width}x${this.height}`);
};

// Usage
const circle = new Circle("red", 5);
const rectangle = new Rectangle("blue", 4, 6);

circle.describe(); // "A red circle with radius 5"
console.log(circle.area()); // 78.54

rectangle.describe(); // "A blue rectangle 4x6"
console.log(rectangle.area()); // 24

// Both are shapes!
console.log(circle instanceof Circle);    // true
console.log(circle instanceof Shape);     // true
console.log(rectangle instanceof Shape);  // true

Key Takeaways

  1. Prototypes are blueprints - They define shared behavior for objects

  2. prototype is on constructors - The blueprint itself

  3. __proto__ is on instances - The reference to the blueprint

  4. new creates and links - It builds the object and sets up the prototype chain

  5. Inheritance without extends - Use Object.create() and .call() for classical inheritance

  6. Events flow in three phases - Capturing → Target → Bubbling

Modern JavaScript Note

While understanding prototypes is crucial, modern JavaScript (ES6+) provides the class syntax as syntactic sugar:

class Vehicle {
    constructor(type) {
        this.type = type;
    }

    accelerate() {
        console.log(`${this.type} is accelerating`);
    }
}

class Motorcycle extends Vehicle {
    constructor(type, brand) {
        super(type);
        this.brand = brand;
    }

    wheelie() {
        console.log(`${this.brand} doing a wheelie!`);
    }
}

Under the hood, this still uses prototypes! The class syntax just makes it cleaner and more familiar to developers from other languages.


Remember: JavaScript's prototype system is like genetic inheritance—objects inherit traits from their prototypes, creating a chain of shared behaviors that makes code efficient and elegant. Master this, and you'll truly understand JavaScript's object-oriented nature!

What aspect of prototypes or events would you like to explore deeper? Drop your questions in the comments below! 🚀