Additionally, inheritance is a common concept in the most modern programming languages, and many times people, even more, experienced programmers, are having difficult times understanding and using it in certain situations.
In order to understand better these concepts, you have to be familiar with the following concepts
- this keyword
For reasons of completeness of this article, I will give a short definition of each of these terms.
The main purpose of a constructor is to create and initialize an object instance. You can call a constructor using the new keyword.
Furthermore, a prototype is a special type of enumerable object itself, so each prototype has its own prototype, making a prototype chain. The chain ends when the last prototype object has null for its own prototype.
Additionally, you can use a prototype as a basis to create other objects.
Each object that you create from the same prototype, will share a similar structure meaning that will contain the properties and methods of its prototype.
In order to have a better understanding of this let’s see some examples.
In the example above, I create two objects named south_china_tiger and indo_chinese_tiger from the same function constructor. So they share a similar structure.
As you can see the following properties numOfLegs, eats, breed are defined in both objects.
Apart from that, in each object, I can define its own properties/methods. So in this case you can see that the property age is defined only for the indo_chinese_tiger.
What if I want to share new properties?
In order to add new properties that will be shared across all the created objects, I can use the prototype property and add them there.
In the above example, you can see that the property age is added to the prototype and is shared across the south_china_tiger and indo_chinese_tiger objects.
As you can see in the above example, the Animal’s prototype property can be accessed using <function-name>.prototype.
On the other hand, an object (instance) does not expose its prototype property. Instead, you can access it using the <object-name>.__proto__ property or with the method Object.getPrototypeOf().
Additionally, you can see that each prototype object has the following properties by default.
Furthermore, as MDN mentions the standard way to access an object’s prototype, since ECMAScript 5 and is available since IE 9, is the Object.getPrototypeOf() method.
Let’s see an example of replacing a prototype object.
In the code above, I first create two objects from the same constructor function and prototype. Next, I am replacing the prototype object with a new object.
As I already mentioned, each object’s prototype is linked to the function’s prototype object.
So what is going to happen if you replace the function’s prototype object, is that only the new objects are going to be linked to the changed prototype object because the new prototype object will reference a brand new address in memory.
On the other hand, the objects that were created before the prototype replacement will still link to the previous prototype of function which is the previous memory address.
Let’s try this time to update a prototype instead of a replacement.
As you can see in the code above, if you update the prototype properties then older and new objects will share the same updated property.
This happens because the prototype object is a single object stored that references a very specific address in memory and is linked to every object created with the same constructor function.
As it is already mentioned, when a new object is created then it inherits all the properties and methods from its prototype. The prototype itself is an object, so it inherits all the properties and methods from its own prototype.
The same process will take place for each prototype object until the property is found or the object will be equal to null which means the end of the prototype chain.
The above code will print in the logs the following
As you can see in the code above, I first define a function constructor named Animal and then an object named south_china_tiger from it.
In order to walk through the prototype chain, I am using a do-while loop. The loop will continue to execute until the prototype object will equal to null.
Let’s see what will happen if you define a property in an object that is predefined in the object’s prototype.
Since I already explained the concept of the prototype chain I can use it to explain what is happening in the above code.
It will find that it is defined there and will stop walking the prototype chain immediately.
This technique is called shadowing the property.
There are various ways so let’s see them by example.
Object literal method
The first method is using object literal.
In the above code, I am using the method Object.setPrototypeOf to set the prototype of my_dog object to the dog’s object.
As you can see, the object my_dogs inherits all the properties and methods from the dogs object while in parallel is preserving its own properties and methods.
In the logs, you can also see that the constructor of the my_dogs object is the Object constructor.
As an alternative to the Object.setPrototypeOf method, I could use the following line of code to set the prototype of the my_dogs object
my_dogs.__proto__ = dogs;
I would not encourage this approach though as I mentioned earlier.
As an alternative I can use the Object.create method too.
In the above, code you can see again, that the my_dogs object inherits all the properties and methods of the dogs object.
If you open the console logs you can see that the prototype of the my_dogs object is still the Object constructor.
In the code above, I create an Animal constructor function with properties num_of_legs, eats, breed, sound. Next, I add the getDetails function to its prototype.
Now I want to create an object with the name Dog that will inherit all of the properties and methods of Animal.
In order to do that, I will use the Dog constructor function that will pass all of its arguments to the Animal constructor function with the help of Animal.call method.
As you can see in the logs, the constructor of the Dog object is the Animal object.
Finally, the Dog object will inherit the getDetails method of its prototype despite the fact that I did not define it in its own properties and methods.
ES6 class method
As you can see in the code above, I have two classes where the class My_Dogs extends the class Dogs.
Each class has a constructor function. The class My_Dogs calls the constructor function of the Dogs class and passes all the required arguments to the Dogs constructor.
Furthermore, in the class My_Dogs I defined the getDetails method.
The object my_dogs will inherit all the properties/methods of the Dogs class plus its own.
Finally, you can notice in the logs that the prototype of My_Dogs class is the Dogs class.
Let’s summarize what you have learned so far
- Each object inherits all the properties and methods of its prototype
- You can modify the prototype object of an object to share properties and methods between objects
- you can use the shadowing technique to override a property or method of a prototype