Achieving Inheritance in JavaScript Without extends: Understanding "__proto__" in JS
In this blog, you'll develop a clear understanding of how the extends keyword functions and how inheritance works under the hood.
In JavaScript, inheritance is usually done using the extends
keyword in classes. But did you know that you can achieve inheritance without extends
? This is possible using the __proto__
property, which links one object to another.
In this blog, we will break down how __proto__
works with super simple examples so that even beginners can understand it!
What is __proto__
?
Every JavaScript object has a hidden property called __proto__
, which points to the prototype of another object. This is how JavaScript enables prototype-based inheritance.
When you try to access a property/method that’s not in the object, JavaScript follows the prototype chain (__proto__
) to find it in its parent. Lets discuss this in details:
Lets understand what happens when you try to access a property which is not defined in the class.
🛠️ Steps Explained in the Flowchart:
- Check Inside the Object Itself
- Check Inside the Class Prototype (
__proto__
)
- Check Inside
Object.prototype
- Reached
null
(End of Prototype Chain)
Inheritance Without extends
Using __proto__
Let's say we have two classes: Person
and Student
. Normally, we would write class Student extends Person
, but here, we’ll use __proto__
instead.
Step 1: Create Classes
// Here I am creating 2 classes
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
class Student {
constructor(name, grade) {
this.name = name;
this.grade = grade;
}
study() {
console.log(`${this.name} is studying in grade ${this.grade}.`);
}
}
So far, Person
has a method greet()
, and Student
has a method study()
. But Student
doesn’t inherit from Person
yet.
Step 2: Link Student
to Person
Using __proto__
We want every student to also have access to greet()
from Person
.
We can achieve this by setting the prototype of Student.prototype
to Person.prototype
:
// Overriding the __proto__ property of Student by Person.prototype.
Student.prototype.__proto__ = Person.prototype;
Now, when a Student
object tries to access a method that isn’t in Student
, JavaScript will look inside Person
.
Step 3: Create Objects and Test
const student1 = new Student("Amit", 10);
student1.study(); // ✅ Output: Amit is studying in grade 10.
student1.greet(); // ✅ Output: Hello, my name is Amit
What Happened Here?
student1.study
()
works becausestudy()
is inStudent
.student1.greet()
works even thoughgreet()
is not inStudent
, because JavaScript looks insidePerson
via__proto__
.
Now, we have successfully achieved inheritance without using extends
! 🚀
Example 2: Multi-Level Inheritance Without extends
Let’s go a step further. Suppose we create another class, HighSchoolStudent
, which should inherit from Student
.
Step 1: Create the Class HighSchoolStudent and Link HighSchoolStudent
to Student
class HighSchoolStudent {
constructor(name, grade, stream) {
this.name = name;
this.grade = grade;
this.stream = stream;
}
prepareForExams() {
console.log(`${this.name} is preparing for final exams in ${this.stream}.`);
}
}
// overriding HighSchoolStudent prototype to Student's
HighSchoolStudent.prototype.__proto__ = Student.prototype;
HighSchoolStudent will inherit from Student, and indirectly from Person.
Step 3: Create Objects and Test
const highSchoolStudent1 = new HighSchoolStudent("Neha", 12, "Science");
highSchoolStudent1.prepareForExams(); // ✅ Output: Neha is preparing for final exams in Science.
highSchoolStudent1.study(); // ✅ Output: Neha is studying in grade 12.
highSchoolStudent1.greet(); // ✅ Output: Hello, my name is Neha.
🎯 How Prototype Chain Works Here
When we call highSchoolStudent1.greet()
, JavaScript searches:
HighSchoolStudent → Student → Person → Object.prototype → null
Since greet()
is found inside Person
, JavaScript calls it successfully!
Checking the Prototype Chain
If you want to check the prototype links, use this:
console.log(HighSchoolStudent.prototype.__proto__ === Student.prototype); // ✅ true
console.log(Student.prototype.__proto__ === Person.prototype); // ✅ true
console.log(Person.prototype.__proto__ === Object.prototype); // ✅ true
console.log(Object.prototype.__proto__ === null); // ✅ true
This confirms that JavaScript follows the prototype chain step by step.
Conclusion
✅ We achieved inheritance without using extends
, just like JavaScript does behind the scenes.
✅ __proto__
allows us to manually link one class to another.
✅ JavaScript follows the prototype chain to find missing methods.
Now that you understand how extends
works internally, try using __proto__
in your own projects!