Prototype and Inheritance in JavaScript


In our previous blog (https://simplejs.hashnode.dev/prototype-the-backbone-of-js-objects), we studied what are prototypes and how they are responsible for validating the statement that “Everything in JavaScript is an object”. We discussed the different do’s and don’ts related to prototypes with a simple layman example.
Recap
In JavaScript, prototypes are the foundation of how inheritance and object behavior sharing work. Unlike classical object-oriented languages (like Java or C++) that use class-based inheritance, JavaScript uses a prototype-based inheritance model.
At its core:
Every JavaScript object has a hidden internal property called
[[Prototype]]
, which refers to another object.
That "other object" is typically called the prototype, and it acts as a template from which properties and methods are inherited.
Inheritance in JavaScript
JavaScript provides us syntactical sugar to use inheritance which resembles to Inheritance used in other languages like Java, CPP etc. Below is an example of it:
class Person {
constructor(name) {
this.name = name;
}
tellName(){
console.log(`my name is ${this.name}`)
}
}
//Now if you want to assign some new methods or Properties to Person, it can be done using:
Person.prototype.age = 23;
Person.prototype.getAge = function () {
console.log(`my age is ${this.age}`);
};
const person1 = new Person("saurav");
person1.tellName(); //my name is saurav
person1.getAge(); //my age is 23
//Now instead of using the syntactical constructor we can do the same thing as:
// Create a blank object
const person2 = {
name: "John",
age: 20
};
// Manually link prototype
person2.__proto__ = Person.prototype;
// Now use prototype methods
person2.tellName(); // my name is John
person2.getAge(); // my age is 20
In the above code same you can see how JS syntactical sugar makes our life easy, but you also observe the use of prototype and __proto__ and the next thing is how are those two different to each other?
__proto__ vs Prototype
In JavaScript both proto and Prototype are related to inheritance feature but have different roles. The confusion could arise because they both sound similar to each other. Let us understand both of them in layman terms.
prototype
– The Blueprint on a FunctionSuppose we are inside a toys factory, and inside the factory we have a blueprint to design toys, now in this context, factory is a JS class or a JS function and the blueprint that is used to build toys is the Prototype.
So Prototype defines the properties and methods of a class or function that will be available to all objects created by that function.
function Toy(name) { this.name = name; } Toy.prototype.sayHi = function () { console.log(`Hi, I'm ${this.name}`); };
Here
sayHi
is added in the blueprint (Prototype) and any object that is created with new Toy(…) will inherit this method as well.__proto__
– The Internal Link on ObjectsNow suppose we have a Toy, but we don’t know how to make it say its name, then we need to check the manual which would contain information about how we can use that feature. That manual is
__proto__
The manual (
__proto__
) acts as a linkage between the blueprint of the toy and the actual toy that we have.const toy1 = new Toy("Alice"); console.log(toy1.__proto__ === Toy.prototype); // true //ideally it is not a good practice to do the below thing even if it works: const toy2.__proto__= Toy.prototype //instead one should do this by using new or Object.create const toy3 = new Toy ("toy3") const toy4 = Object.create(Toy.prototype) toy4.name ="toy4"
Difference between Prototype and proto:
Concept | __proto__ | prototype |
Belongs to | Objects (instances) | Functions (constructors) |
Purpose | Points to the object's prototype | Used to build the prototype for new objects |
Accessed via | object.__proto__ | ConstructorFunction.prototype |
Role | Runtime linkage in the prototype chain | Blueprint for instance methods and properties |
Editable? | Yes (but rarely recommended to modify) | Yes (used to define shared behavior) |
Parent Child Inheritance in JavaScript
Like other languages, JavaScript also supports inheriting features from one class or function to the other and provides a syntactical sugar to do it.
class Human {
constructor(name, gender) {
this.name = name;
this.gender = gender;
this.noOfTeeth = 32;
}
askGender() {
console.log(`My gender is ${this.gender}`);
}
askName() {
console.log(`My name is ${this.name}`);
}
}
class Man extends Human {
constructor(name, strength) {
super(name, "male"); // call parent constructor with gender
this.strength = strength;
}
askStrength() {
console.log(`My strength is ${this.strength}`);
}
}
class Woman extends Human {
constructor(name, kindness) {
super(name, "female"); // call parent constructor with gender
this.kindness = kindness;
}
askKindness() {
console.log(`My kindness is ${this.kindness}`);
}
}
const john = new Man("John", 80);
john.askName(); // My name is John
john.askGender(); // My gender is male
john.askStrength(); // My strength is 80
console.log(john.noOfTeeth); // 32 (inherited)
const emma = new Woman("Emma", "very kind");
emma.askName(); // My name is Emma
emma.askGender(); // My gender is female
emma.askKindness(); // My kindness is very kind
console.log(emma.noOfTeeth); // 32 (inherited)
In the above example Man and Woman are two classes which are inheriting from a parent class Human. To call a parent constructor from a child, one needs to use super
keyword.
Now we can do the same thing without using the syntactical sugar provided by JavaScript, however the below approach is not recommended.
const humanProto = {
noOfTeeth: 32,
askName() {
console.log(`My name is ${this.name}`);
},
askGender() {
console.log(`My gender is ${this.gender}`);
}
};
const manProto = {
askStrength() {
console.log(`My strength is ${this.strength}`);
}
};
// Link manProto's prototype to humanProto
manProto.__proto__ = humanProto;
// Create a new object and link to manProto
const rawMan = {
name: "John",
gender: "male",
strength: 90
};
rawMan.__proto__ = manProto;
rawMan.askName(); // My name is John
rawMan.askGender(); // My gender is male
rawMan.askStrength(); // My strength is 90
console.log(rawMan.noOfTeeth); // 32
Conclusion
This is all about how prototypes work behind the scenes in enabling inheritance feature for JavaScript, In the next blog, we would be looking more about class and inheritance in JavaScript using the syntactical sugar that it provides us. We are now well aware of the core of things and should move towards a higher level now.
Until then keep exploring and coding.
Subscribe to my newsletter
Read articles from Saurav Pratap Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
