Inheritance, a prominent feature of a programming language, emerged with the introduction of object-oriented programming languages. Most of these languages were class-based languages. Here, class is like a plan or blueprint and objects are its manifestation. Meaning, in order to create an object, first we have to create a class. Then we can create any number of objects from one class.
Imagine, we have a class that represents a smartphone. This class has features like capturing images, GPS, etc, like any other smartphone. Here’s an example of how to create such a class and an object in C++ :
We created a class named
SmartPhone and it has a method named
capturePictures, to capture images.
Let’s imagine, we need an iPhone class, that would capture images along with some special features like a face ID scan. Here are two possible solutions:
- Rewrite the
captureImagesfeature along with other common smartphone features, plus iPhone specific features into a new class. But this approach takes more time, effort and can introduce more bugs.
- Reuse features from the
SmartPhoneclass. This is where inheritance comes into play. It’s a way to reuse features from other classes/objects.
Here is how we can inherit
capturePictures method from the
SmartPhone class, in our new
Iphone class, in C++ :
Above is a trivial example of inheritance. However, it shows that inheritance allows us to reuse code in a way that the resulting program is less error-prone and takes less time to develop.
Here are some important things to know about classes :
- A class that inherits the feature is called as a child class
- A class from which features are inherited is called a parent class
- A class can inherit from multiple classes at once. For instance, class C inherits from class A and class B
- We can have multiple levels of Inheritance. For instance, class C inherits from class B and, class B inherits from class A
What is a prototype?
[[Prototype]] is linked to an object, that object has its own
[[Prototype]] reference. This is how a chain is built (it’s known as the prototype chain.
This chain of
To access the object’s
[[Prototype]], most of the browsers provide a
This is how we can access it:
It’s important to note that, this property is not a part of the ECMAScript standard. It is a de-facto implementation by the browsers.
Get and set prototype methods
Apart from the
__proto__ property, there is a standard way to access the
Here is how we can access the
[[Prototype]] of an object:
There is a similar method to set the
[[Prototype]] of an object. This is how we do it:
We have now discussed
[[Prototype]]. It’s nothing but a standard notation to designate the prototype of an object. Many developers get it confused with .prototype property, which is an entirely different thing.
Let’s explore the
new keyword like this:
When you console.log the
phone object, you will see an object with
__proto__ property, like this:
Now, if we want to have some methods on the phone object, we can use
.prototype property on the function, as follows:
When we create the phone object again, we would see the following in the
We can see the
isAndroid() method in the object’s
In short, the
.prototype property is basically like a blueprint for the
[[Prototype]] object created by the given constructor function. Anything that you declare in the
.prototype property/object will pop up in object’s
As a matter of fact, if you compare the
SmartPhone.prototype to the phone’s
[[Prototype]], you will see that they are the same:
It’s worth noting that, we can also create methods inside the constructor function. Instead, we did it using the function’s prototype. There’s a good reason to do so.
Let’s take a look at the following example:
The problem with this approach is when we initiate a new object. All the instances get their own copy of
methodA. On the contrary, when we create it on function’s prototype, all instances of the object share just one copy of the method. Which is more efficient.
What happens when we access a property?
When we access a property either to get it, the following happens:
- If it finds the property, then it returns it
- If the property is found, then it returns it
- Otherwise, it looks into
[[Prototype]]. This chain ends when either the property is found or there is no
[[Prototype]]left, which means that we have reached the end of the prototype chain
[[Prototype]] chain. Here is an example:
In the above example, we created a constructor function, which has a property
propA on it’s
[[Prototype]] directly, we can’t. It’s called the shadowing of property.
It’s also worth noting that the end of a normal object’s
[[Prototype]] chain is built-in
Object.prototype. That’s the reason why most of the object shares many methods like
toString(). Because they are actually defined on
Various ways of using prototypical inheritance
If we log the obj in the browser’s console, we will see the following:
So basically, all the objects created with literal notation inherit properties from
It’s also worth noting that
__proto__ object has reference to the constructor function, from which it’s created. In this case, the
constructor property points to
Using the Object constructor
Another, not-so-common way of creating an object is using
Object to create Objects.
Here’s how we use it:
This approach results in the same object as object literal notation. It inherits properties from
Object.prototype. Since we use
Object as a constructor function.
With this helper method, we can create an object with another object as it’s
[[Prototype]] like this:
Any guess how we can make an
object without any
Now, we want to create an iPhone class, which should have
'iOS' as it’s OS. It should also have the
First, we have to create an
Iphone constructor function and inside it, we should call the
SmartPhone constructor, like this:
This will set the
this.os property to
'iOS' in the
Iphone constructor function.
The reason why we called
SmartPhone.call method is because we need to change the value of
this to refer to
Iphone. It would be similar to calling the parent’s constructor in an object-oriented world.
The next thing is, we have to inherit methods from
SmartPhone constructor. We can use our
Object.create friend here, as follows:
Now we can add methods for
.prototype as follows:
Finally, we can create an object using
Iphone as follows:
With the ES6, this whole ordeal is very simple. We can create classes (they are not the same as classes in C++ or other any class-based language, just a syntactical sugar on top of prototypical inheritance) and derive new classes from other classes.
Here is how we create a class in ES6:
Now we can create a new class which is derived from
SmartPhone, like this :
Instead of calling
SmartPhone.call, we are calling
Finally, we can create an object using
Iphone as follows:
This ES6 example is the same as the previous constructor method example. But it’s much cleaner to read and understand.
Let’s summarize what we have learned so far:
- In class-based languages, we can’t run the classes. We have to create objects from them in order to get anything done
[[Prototype]]is just a fancy way of referring to an object’s prototype. They’re both the same thing
- We can access an object’s prototype using either
- We found out that the function’s prototype property acts as a blueprint for the object’s
[[Prototype]]which is created using the
- We learned what happens when we access a property on an object and what role the prototype chain plays there