Understanding Prototypes & Inheritance in JavaScript: The Blueprint Analogy
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:
Each object gets its own property storage -
house1.addressandhouse2.addressare separateThey share the same prototype methods - Both point to
House.prototype.openDoor(same memory location!)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:
Capturing Phase: The sound travels DOWN from the building entrance to your apartment
Target Phase: You answer the door
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
Prototypes are blueprints - They define shared behavior for objects
prototypeis on constructors - The blueprint itself__proto__is on instances - The reference to the blueprintnewcreates and links - It builds the object and sets up the prototype chainInheritance without
extends- UseObject.create()and.call()for classical inheritanceEvents 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! 🚀