Prototype and Inheritance in JS

Kartikey KatyalKartikey Katyal
10 min read

In programming, inheritance refers to passing down characteristics from a parent to a child so that a new piece of code can reuse and build upon the features of an existing one. JavaScript implements inheritance by using objects. Each object has an internal link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. By definition, null has no prototype and acts as the final link in this prototype chain.

Prototype Inheritance – The Family Genetics Analogy πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸ§¬

Imagine a family where physical traits and skills pass down from one generation to the next. This is just like how JavaScript objects inherit properties and methods from their prototype.

Meet Grandpa Tom – The Original Prototype πŸ‘΄πŸ§¬

Grandpa Tom is the origin of the family traits. He has some special characteristics:
βœ… Blue eyes
βœ… Curly hair
βœ… Good at woodworking

Since Grandpa Tom is the first in the lineage, these traits become the prototype traits of the family.


Dad Mike Inherits from Grandpa πŸ§‘

Dad Mike is Grandpa Tom’s son. He inherits most of his father’s traits automatically:
βœ” Blue eyes (Inherited from Grandpa)
βœ” Curly hair (Inherited from Grandpa)
βœ” Good at woodworking (Inherited from Grandpa)
βž• Great at painting (New skill, not from Grandpa)

Even though he didn’t explicitly define "blue eyes" or "curly hair," they are inherited from Grandpa.


Little Timmy – The Next Generation πŸ‘¦

Now, Dad Mike has a son, Little Timmy. He automatically inherits traits, but he can also have his own modifications.

βœ” Blue eyes (Inherited from Grandpa Tom)
βœ” Curly hair (Inherited from Grandpa Tom)
βœ” Good at woodworking (Inherited from Grandpa Tom)
βœ” Great at painting (Inherited from Dad Mike)
βž• Loves coding (New unique trait of Timmy)

Timmy doesn't redefine his eye color or hair type because he inherits them from his ancestors. However, he adds his own unique skill (coding).


How This Relates to JavaScript Prototypes

  • Grandpa Tom = Base prototype (e.g., Object.prototype)

  • Dad Mike = Intermediate prototype (e.g., Parent Object like Animal.prototype)

  • Little Timmy = Child object (Specific instance, like Dog)

  • Adding a new trait (Timmy loves coding) = Overriding or adding new properties in an object

  • If a trait isn’t found in Timmy, JavaScript looks at Dad, then Grandpa (Prototype Chain)


Code Example Based on This Story

javascriptCopyEdit// Grandpa Tom - Base Prototype
function Grandpa() {
    this.eyeColor = "Blue";
    this.hairType = "Curly";
}

Grandpa.prototype.woodworkingSkill = function() {
    console.log("I am great at woodworking!");
};

// Dad Mike - Inheriting from Grandpa
function Dad() {
    Grandpa.call(this); // Inherit properties
    this.extraSkill = "Painting"; // Unique to Dad
}

// Inherit prototype methods
Dad.prototype = Object.create(Grandpa.prototype);
Dad.prototype.constructor = Dad;

// Timmy - Inheriting from Dad
function Timmy() {
    Dad.call(this); // Inherit properties
    this.uniqueSkill = "Coding"; // Unique to Timmy
}

// Inherit prototype methods
Timmy.prototype = Object.create(Dad.prototype);
Timmy.prototype.constructor = Timmy;

// Creating instances
const timmy = new Timmy();

console.log(timmy.eyeColor); // "Blue" (Inherited from Grandpa)
console.log(timmy.hairType); // "Curly" (Inherited from Grandpa)
console.log(timmy.extraSkill); // "Painting" (Inherited from Dad)
console.log(timmy.uniqueSkill); // "Coding" (Timmy's own skill)

timmy.woodworkingSkill(); // "I am great at woodworking!" (Inherited from Grandpa)

Prototype Chain in Action πŸ—οΈ

  1. When JavaScript looks for timmy.eyeColor, it doesn’t find it in Timmy.

  2. It looks up in Dad's prototype – still not found.

  3. It looks up in Grandpa's prototype and finds it there β†’ "Blue".

prototype vs __proto__ in JavaScript

In JavaScript, both prototype and __proto__ are related to prototype-based inheritance, but they serve different purposes. Let’s break it down clearly.


1️⃣ prototype (Function Prototype)

πŸ”Ή Exists on functions (only on constructor functions, NOT regular objects).
πŸ”Ή Used to define shared properties/methods for all objects created from a constructor.
πŸ”Ή Allows objects created from a function to inherit methods and properties.

Example of prototype

javascriptCopyEdit// Constructor function
function Person(name) {
    this.name = name;
}

// Adding a method using prototype
Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name}`);
};

// Creating objects
const john = new Person("John");
const alice = new Person("Alice");

// Both objects inherit greet from Person.prototype
john.greet(); // Output: Hello, my name is John
alice.greet(); // Output: Hello, my name is Alice

πŸ”Ή Here, Person.prototype is used to store the greet method, and all instances of Person inherit it.
πŸ”Ή This avoids copying the method for every object, making memory usage efficient.


2️⃣ __proto__ (Dunder Proto)

πŸ”Ή Exists on every object and points to its prototype (inherited object).
πŸ”Ή Used for prototype chain lookups when accessing properties or methods.
πŸ”Ή It’s an accessor to the internal prototype, but modifying it directly is discouraged.

Example of __proto__

javascriptCopyEditconst obj = { key: "value" };

console.log(obj.__proto__); // Points to Object.prototype
console.log(obj.__proto__ === Object.prototype); // true

You can also manually set __proto__, though it’s not recommended:

javascriptCopyEditconst parent = { greet: function() { console.log("Hello!"); } };
const child = {};

child.__proto__ = parent; // Manually setting prototype
child.greet(); // Output: Hello! (Inherited from parent)

πŸ”Ή Better alternative to setting __proto__ directly:

javascriptCopyEditconst child = Object.create(parent);
child.greet(); // Output: Hello!

This properly sets up inheritance.


Key Differences Between prototype and __proto__

Featureprototype__proto__
TypeProperty of functions (only function objects have it)Property of all objects
PurposeUsed to define methods and properties for instancesUsed to access the prototype (inherited object)
UsageDefines shared methods for objectsHelps in prototype chaining (property lookup)
Modifiable?Yes, you can add methods to itCan be modified, but not recommended
ExamplePerson.prototype.greet = function() {...}child.__proto__ = parent (not recommended)

Final Analogy 🎭

  • prototype is like a recipe book for a restaurant chain:

    • Every restaurant (object) follows the same recipe (method stored in prototype).

    • If you change the recipe (prototype), it updates for all future restaurants.

  • __proto__ is like asking your parents about a tradition:

    • If you (object) don’t have a specific skill (property), you ask your parents (__proto__) if they do.

    • If they don’t have it, they ask their parents (prototype chain continues).


When to Use What?

βœ… Use prototype when defining shared methods in constructor functions.
βœ… Use Object.create(prototypeObject) instead of modifying __proto__.
❌ Avoid modifying __proto__ directly because it’s slower and considered bad practice.

Achieving Inheritance in JavaScript Without Using extends

Even without the extends keyword (which is used in ES6 classes), JavaScript still allows inheritance through prototypes. Below are different ways to achieve inheritance without using extends.


1️⃣ Using Constructor Functions and Object.create()

This is the classic way to achieve inheritance before ES6 classes.

Example:

javascriptCopyEdit// Parent Constructor Function
function Animal(name) {
    this.name = name;
}

// Adding method to Animal prototype
Animal.prototype.makeSound = function() {
    console.log("Some generic animal sound");
};

// Child Constructor Function
function Dog(name, breed) {
    Animal.call(this, name); // Inherit properties from Animal
    this.breed = breed;
}

// Inheriting methods from Animal prototype
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// Adding a new method specific to Dog
Dog.prototype.bark = function() {
    console.log("Woof! Woof!");
};

// Creating an instance
const myDog = new Dog("Buddy", "Golden Retriever");

console.log(myDog.name); // Buddy (Inherited property)
myDog.makeSound(); // Some generic animal sound (Inherited method)
myDog.bark(); // Woof! Woof! (Dog's own method)

How It Works?

  1. Animal.call(this, name) copies the properties from Animal to Dog.

  2. Dog.prototype = Object.create(Animal.prototype) links Dog to Animal for prototype-based inheritance.

  3. Now, myDog inherits makeSound() from Animal but can also have its own bark() method.


2️⃣ Using Object.create() Directly (Prototypal Inheritance)

This approach creates objects without using constructor functions.

Example:

javascriptCopyEdit// Parent object
const Animal = {
    makeSound: function() {
        console.log("Some generic animal sound");
    }
};

// Creating a new object and inheriting from Animal
const Dog = Object.create(Animal);
Dog.bark = function() {
    console.log("Woof! Woof!");
};

// Creating an instance
const myDog = Object.create(Dog);
myDog.makeSound(); // Inherited from Animal: "Some generic animal sound"
myDog.bark(); // Woof! Woof!

How It Works?

  • Object.create(Animal) sets Animal as the prototype of Dog.

  • Object.create(Dog) sets Dog as the prototype of myDog.

  • This creates a prototype chain where myDog first looks for bark(), and if not found, it looks in Animal.


3️⃣ Using Class-Like Syntax Without extends

Even though we're not using extends, we can still create a class-like structure using constructor functions and prototypes.

Example:

javascriptCopyEdit// Parent class (function-based)
function Animal(name) {
    this.name = name;
}

// Adding method to prototype
Animal.prototype.makeSound = function() {
    console.log(`${this.name} makes a sound`);
};

// Child class without using extends
function Dog(name, breed) {
    Animal.call(this, name); // Call parent constructor
    this.breed = breed;
}

// Manually set up prototype inheritance
Object.setPrototypeOf(Dog.prototype, Animal.prototype);

// Adding new method to Dog
Dog.prototype.bark = function() {
    console.log(`${this.name} barks: Woof! Woof!`);
};

// Creating an instance
const myDog = new Dog("Buddy", "Golden Retriever");
myDog.makeSound(); // Buddy makes a sound
myDog.bark(); // Buddy barks: Woof! Woof!

How It Works?

  • Animal.call(this, name) calls the parent constructor.

  • Object.setPrototypeOf(Dog.prototype, Animal.prototype) manually sets up the inheritance.

  • myDog gets access to both makeSound() from Animal and bark() from Dog.

βœ… Object.create() β†’ Best for pure prototype-based inheritance.
βœ… Constructor Functions with Object.create(Parent.prototype) β†’ Closest to ES6 extends.
βœ… Object.setPrototypeOf() β†’ Explicitly sets up inheritance but is less common.

JavaScript Events Explained with a Real-World Analogy πŸš¦πŸŽ‰

Think of JavaScript events like real-world events happening around you. Just like how we listen and respond to events in daily life, JavaScript can detect and react to user interactions like clicks, keypresses, or mouse movements.


🎈 Real-World Analogy: A Birthday Party πŸŽ‚

Imagine you are at a birthday party πŸŽ‰. Different events happen at the party, and depending on what happens, you take action.

1️⃣ Event: Someone Rings the Doorbell πŸšͺπŸ””

  • Real-world: When a guest arrives, they ring the doorbell.

  • JavaScript: This is like a "click" event on a button.

  • Response: You hear the bell and open the door (event handler runs).

javascriptCopyEditdocument.getElementById("doorbell").addEventListener("click", function() {
    console.log("Someone rang the doorbell! Open the door.");
});

2️⃣ Event: Someone Blows Out the Birthday Candles πŸŽ‚πŸ’¨

  • Real-world: When the birthday person blows out the candles, people clap.

  • JavaScript: This is like a "keypress" event (e.g., pressing Enter to submit a form).

  • Response: People clap and cheer (event handler executes).

javascriptCopyEditdocument.addEventListener("keypress", function(event) {
    if (event.key === "Enter") {
        console.log("πŸŽ‰ The candles are blown out! Everyone claps!");
    }
});

3️⃣ Event: The Music Starts Playing 🎡🎧

  • Real-world: When the DJ starts playing music, people start dancing.

  • JavaScript: This is like a "change" event (e.g., selecting a different song from a dropdown).

  • Response: People react to the music change.

javascriptCopyEditdocument.getElementById("musicSelect").addEventListener("change", function() {
    console.log("🎢 New song selected! People start dancing!");
});

4️⃣ Event: Someone Spills a Drink πŸΉπŸ’¦

  • Real-world: When someone spills a drink, a waiter quickly cleans it up.

  • JavaScript: This is like an "error" event (e.g., an image fails to load).

  • Response: A waiter (event handler) jumps in to fix it.

javascriptCopyEditdocument.getElementById("partyImage").addEventListener("error", function() {
    console.log("Oops! The image didn't load. Show a backup image.");
});

5️⃣ Event: A Guest Leaves the Party πŸšΆβ€β™‚οΈπŸ 

  • Real-world: When someone says goodbye, the host thanks them for coming.

  • JavaScript: This is like a "mouseleave" event (e.g., moving the mouse away from an element).

  • Response: Display a "Thanks for visiting!" message.

javascriptCopyEditdocument.getElementById("guestArea").addEventListener("mouseleave", function() {
    console.log("Thanks for coming! See you next time!");
});

Summary Table πŸ“‹

Real-World EventJavaScript EventResponse (Event Listener)
Ringing the doorbell πŸšͺπŸ””clickOpen the door
Blowing out candles πŸŽ‚πŸ’¨keypress (Enter)People clap
Changing music 🎡🎧changePeople dance
Spilling a drink πŸΉπŸ’¦errorWaiter cleans up
Guest leaving πŸšΆβ€β™‚οΈmouseleaveHost says goodbye

  • Events are things that happen in the browser, just like real-life events.

  • JavaScript lets us listen for these events and respond accordingly.

  • We use event listeners (addEventListener) to trigger actions when events occur.

2
Subscribe to my newsletter

Read articles from Kartikey Katyal directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kartikey Katyal
Kartikey Katyal