Mastering JavaScript Prototypes: Unraveling __proto__ and prototype

Santwan PathakSantwan Pathak
9 min read

🔰Introduction

JavaScript’s object system is often misunderstood, especially when it comes to inheritance. Unlike class-based languages, JavaScript uses prototypes to link objects and share properties efficiently. At the core of this mechanism are __proto__ and prototype, two fundamental concepts that define how objects inherit behaviors in JavaScript.

Understanding these concepts is crucial for writing optimized, maintainable, and scalable JavaScript code. Whether you're a beginner looking to grasp the basics or an experienced developer aiming to solidify your understanding, this guide will walk you through:

What is __proto__? The internal reference that links an object to its prototype.
How does the prototype chain work? The step-by-step lookup process for inherited properties.
What is prototype? The blueprint for defining shared methods in constructor functions.
Key differences between __proto__ and prototype to clear up common misconceptions.

By the end of this article, you'll have a clear, practical understanding of how JavaScript handles inheritance under the hood. 🚀

Let’s dive in! 🔥Demystifying __proto__ and Prototype in JavaScript: A Comprehensive Guide to Object Inheritance, Prototype Chaining, and How JavaScript Shares Behavior

🔰Understanding __proto__ and prototype

Imagine you are part of a family. You inherit certain traits from your parents, like eye color or hair type. Now, your parents inherited their traits from your grandparents, and so on. This chain of inheritance continues up the family tree.

In JavaScript, objects work the same way! Instead of inheriting physical traits, they inherit properties and methods from other objects through a mechanism called prototype chaining.

For example:

  • If you don’t know how to drive but your parent does, you can "borrow" that skill from them.

  • If your parent doesn’t know but your grandparent does, you inherit from them instead.

  • This continues until you reach the top-most ancestor.

In JavaScript, the parent of an object is its prototype, and __proto__ is the link that connects an object to its prototype.

💡 In simple terms: If JavaScript doesn’t find a property on an object, it looks at its prototype (parent). If it’s not found there, it looks at the prototype’s prototype (grandparent), and this continues until it reaches null, which is the root ancestor.

With this analogy, let’s now explore how JavaScript implements this inheritance using __proto__ and prototype chaining! 🚀

❇️ What is __proto__ ?

__proto__ is an internal property in JavaScript that links an object to its prototype. It allows objects to inherit properties and methods from other objects via prototype chaining.

__proto__ is a property, not a keyword, function, or method. It is an internal reference that links an object to its in-built prototype.

🔹 Prototype Chain & Inheritance

Every JavaScript object has a prototype from which it can inherit properties. When accessing a property or method on an object, JavaScript first looks for it on the object itself. If not found, it checks the object's prototype, then the prototype's prototype, and so on until it reaches null. This is called prototype chaining.

🔹 Example:

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

const user = {
  name: "Anushka"
};

user.__proto__ = person; // Setting person as the prototype of user

console.log(user.name);  // Alice (from user)
user.greet();            // Hello! (inherited from person)

🔹 How Prototype Chain Works

console.log(user.__proto__);         // Points to 'person'
console.log(user.__proto__.__proto__); // Points to Object.prototype
console.log(user.__proto__.__proto__.__proto__); // null (end of the chain)

🔹Prototype Chain Diagram

child → parent → grandparent → Object.prototype → null
  • __proto__ was not originally part of the ECMAScript standard, but browsers implemented it for debugging.

  • It is now standardized in ECMAScript 2015 (ES6) but mainly for legacy compatibility.

  • Modern JavaScript prefers Object.getPrototypeOf(obj) and Object.setPrototypeOf(obj) instead.


❇️ Now, What is prototype in JavaScript?

The prototype in JavaScript is an object that is associated with a constructor function and is used to define properties and methods that should be shared by all instances created from that constructor.

A constructor function is just a regular function that is used to create multiple objects with the same structure. It acts like a blueprint for objects. Although it is an old way to do that.

Here is an example

function Person(name, age) {
  this.name = name;
  this.age = age;
}

👉 Person is a constructor function.
👉 When you call new Person("John", 30), it creates a new object with name and age properties.

💡Key Facts About prototype:

  • prototype is an object that belongs to functions and is used to define shared properties/methods.

  • Objects created using a constructor function inherit from that function’s prototype.

  • Not every object has a prototype, but every function has a prototype property.

🔷 Understanding prototype with an Example

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

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

// Creating instances
const dog = new Animal("Dog");
const cat = new Animal("Cat");

dog.speak(); // Output: Dog makes a sound
cat.speak(); // Output: Cat makes a sound

🔷 Flow of the above Code segment

  • The function Animal has a prototype object.

  • When new Animal("Dog") is executed:

    • A new object is created.

    • This object’s __proto__ is set to Animal.prototype.

  • When dog.speak() is called:

    • JavaScript first looks for speak inside dog.

    • If not found, it checks dog.__proto__ (which is Animal.prototype).

    • It finds speak there and executes it.

🔷 How prototype Works Internally

Every function (that is not an arrow function) in JavaScript automatically gets a prototype property, which is an object.

  •       function Example() {}
    
          console.log(typeof Example.prototype); // "object"
          console.log(Example.prototype); // { constructor: Example }
    

    By default, Example.prototype contains:

  •       {
            constructor: Example
          }
    

    You can add methods and properties to this prototype object.

🔷 Fundamental Difference b/w proto and prototype

Feature__proto__prototype
What it isA reference (pointer) to the prototype of an objectA property of constructor functions that defines the prototype of objects created from it
Belongs toEvery object in JavaScriptOnly functions (specifically constructor functions)
TypeObjectObject
PurposeLinks an object to its prototypeDefines methods and properties shared across all instances created by a constructor function
Used byInstances (objects)Constructor functions
Accessed viaobj.__proto__ or Object.getPrototypeOf(obj)ConstructorFunction.prototype
Modifiable?Yes, but modifying it is not recommendedYes, used to define shared behavior

🔹prototype is a Property of Constructor Functions

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

Person.prototype.sayHello = function() {
  console.log(`Hello, I am ${this.name}`);
};
  • Person.prototype is an object that contains sayHello.

  • Any object created using new Person() will inherit from Person.prototype.

🔹__proto__ is a Hidden Property in Instances

const john = new Person("John");

console.log(john.__proto__ === Person.prototype); //Output:  true
console.log(Object.getPrototypeOf(john) === Person.prototype); //Output: true
  • john.__proto__ points to Person.prototype, meaning john inherits properties from it.

  • The prototype object of Person becomes the __proto__ of instances created by new Person().

🔹Conceptual View

john ---> Person.prototype ---> Object.prototype ---> null
  • john.__proto__ → Points to Person.prototype

  • Person.prototype.__proto__ → Points to Object.prototype

  • Object.prototype.__proto__null (end of prototype chain)

One Line Summary
💡 "In JavaScript, every object has an internal property called proto, which links it to the prototype object of its constructor function. The constructor function itself has a prototype property, which defines shared methods. This mechanism allows objects to inherit properties and methods, forming a prototype chain."

🔰One Shot Summary

🔷 Fundamental Relationships

  • Every JavaScript object has an internal link (__proto__) to its prototype.

  • A function (constructor) has a prototype object that is inherited by all instances created from it.

  • __proto__ is an object's reference to its prototype, while prototype is a property of constructor functions.

🔷 How They Are Connected

Think of it like a chain:

  1. Every object in JavaScript has an internal property called __proto__.

  2. This __proto__ points to the prototype object of the constructor function that created it.

  3. The constructor function itself has a special prototype property, which is an object that holds shared methods and properties.

  4. This process forms the Prototype Chain, which allows inheritance in JavaScript.

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

Person.prototype.greet = function() {
  console.log(`Hello, I am ${this.name}`);
};

const virat = new Person("Virat");

console.log(virat.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true

Step 1: virat.__proto__ === Person.prototype

👉 virat is an instance of Person created using new Person().
👉 Every object created by a constructor function gets its __proto__ set to the constructor’s prototype.
🔹 Conclusion: virat.__proto__ and Person.prototype are the same.

Step 2: Person.prototype.__proto__ === Object.prototype

👉 Person.prototype itself is an object.
👉 All objects in JavaScript (except null) inherit from Object.prototype.
🔹 Conclusion: Person.prototype.__proto__ points to Object.prototype, showing that custom objects ultimately inherit from JavaScript's built-in Object.

Step 3: Object.prototype.__proto__ === null

👉 Object.prototype is the top-most prototype in JavaScript.
👉 It has no prototype above it, so its __proto__ is null.
🔹 Conclusion: The prototype chain ends at null, confirming that there’s no prototype beyond Object.prototype.

🎉 Congratulations! 🎉

You've made it this far, and that means you’ve unlocked a solid understanding of JavaScript’s prototype chain! 🚀

I hope that after all these discussions, you now feel confident about how objects, prototypes, __proto__, and constructor functions work together under the hood.

But learning never stops! Do you feel like you’ve grasped the concept? Or do you have any lingering doubts? 🤔

Drop your thoughts in the comments—I’d love to hear your take! 💬👇

🔴 But Wait… Is This Approach Perfect?

While prototype-based inheritance is powerful, it comes with a few drawbacks:

1️⃣ Complex Syntax & Readability Issues

  • The prototype and __proto__ chain can be confusing for beginners.

  • Manually setting prototypes (obj.__proto__ = anotherObj) is not intuitive.

2️⃣ Inheritance is Not Explicit

  • Unlike other languages that use class, JavaScript’s prototype-based inheritance is implicit.

  • Understanding how properties/methods are inherited requires tracing through multiple prototype levels.

3️⃣ Modifying __proto__ Directly is Bad Practice

  • Changing __proto__ directly is slow and discouraged because it impacts performance.

  • Instead, Object.create() should be used to set prototypes.

4️⃣ Boilerplate Code for Constructor Functions

  • Defining constructor functions and manually attaching methods to prototype takes extra lines of code.

  • There’s no built-in super keyword like in ES6 classes.

💡 This is where modern JavaScript solutions come in!

To make things cleaner, ES6 introduced class syntax, which simplifies inheritance while keeping the power of prototypes.

🔜 What’s Next?

In the next blog, we’ll explore:
✔️ How class simplifies prototype-based inheritance
✔️ Using Object.create() for prototype-based inheritance without constructor functions
✔️ How extends and super improve inheritance structure

👉 Next up: "Modern JavaScript Inheritance: Classes, Object.create(), and More!"

Stay tuned! 🚀

20
Subscribe to my newsletter

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

Written by

Santwan Pathak
Santwan Pathak

"A beginner in tech with big aspirations. Passionate about web development, AI, and creating impactful solutions. Always learning, always growing."