Prototypical inheritance is one of the core concepts in JavaScript.
But at the same time it is considered as one of the tough topics to understand. Even the experienced JS developers fall short in confidence while dealing with inheritance/prototypical inheritance in JS.
But to be honest, prototypical inheritance doesn't have any room for ambiguity, and it's pretty straightforward to understand.
In this post, I will deep dive into the core concepts of JS related to prototype. What exactly is a prototype? What is the role of the
newkeyword in prototypical inheritance, and why is this inheritance called prototypical inheritance? Internal working and ES6 updates regarding prototypical inheritance.
Table of Contents
- The Confusion
newkeyword and its role in prototypical inheritance- Objects in JS
- Inheritance chain (instance and static)
- ES6 updates
The Confusion:
-
The biggest confusion that almost everyone faces while trying to understand the prototypical inheritance is the word
prototypeitself.
Because this is actually confusing, as per the ECMA docs, the word prototype is used in two different places, and that too, to represent two different entities altogether
One is the [[prototype]], and the other is the property namedprototype -
Now let's settle the dust.
-
[[prototype]] refers to the
__proto__property. Every object in JavaScript has a default, built-in property named__proto__. And the same__proto__property represents the [[prototype]] mentioned in the docs. For the sake of simplicity, in this post, whenever I want to refer to this [[prototype]], I will always refer to it as__proto__. - The property named
prototype- In JavaScript, every function created by default has a property namedprototypeattached to its object. (Yes, a function is also an object in JavaScript, but a special kind of object; we will discuss the specialty of this object later in this post.)
Think of it like a function is an object, and it has a key namedprototypeon it. We didn't add this key to the function object; it's present by default. And the value of this key is again an object. -
[[prototype]] refers to the
So a function object in JS has 2 properties by default.
One is__proto__because a function is also an object, and every object has__proto__
And the other isprototypebecause it's a function.
In simple terms, only function objects get access to a special default key named prototype along with __proto__
This might sound confusing; let's take an example
function add(a, b) {
return a+b
}
When the JS engine sees this instruction, it actually creates an object in the background. And since this object is a function object, as discussed, it will have a default property added to it named
prototype
Something like this:
{
name: add, // name of the function
length: 2, // referring to 2 parameters the function add has
prototype: { // this key is added by default as add is a function
// .... (some default key-value pairs would be present, we will see them later)
__proto__:Object.prototype // wait what ? what is this ?
}
__proto__: Function.prototype // again wait what ? what is this ?
}
NOTE:
Wherever we see an object ({}), we see a
__proto__being present, because every object in JS has a__proto__property
Now, add is just a reference to this object present in memory, and using add we can access this object.
For example, we can add custom key-value pairs to this object.
add.myCustomKey = 10;
add.myCustomFn = function() { console.log('Add custom function') }
console.log(add.myCustomKey) // 10
add.myCustomFn(); // Add custom function
NOTE:
If you observed, there were a couple of places where
__proto__was used above in the function object, and we had no idea why at one placeObject.prototypeis present and why at the other placeFunction.prototypeis present. And that's completely fine. We will clear up this confusion in the coming sections.
- To summarize, every object in JavaScript has a built-in property named
__proto__and since a function is also an object in JavaScript, even the function object also has a__proto__property, but as discussed, since a function is a special object, apart from the built-in__proto__property, it also has one more special property namedprototype, which is again an object.
Mental Model:
- If object, then has access to the
__proto__property. - If the object is a function object, then it has access to the
__proto__andprototypeproperties.
let obj = {}
function add(a, b) {
return a+b;
}
console.log(obj.__proto__) // available
console.log(add.__proto__) // available
console.log(add.prototype) // available because add is a function
console.log(add.prototype.__proto__) // avaialbe, because prototype itself is an object, and every object has __proto__ property
// All these properties are present by deafult. Added by langugage
- So from now on in this post, when
__proto__is referred to, we are talking about the default property present in every object, and whenprototypeis referred to, we are talking about the default property present in a function object.
new keyword and its role in prototypical inheritance:
The
newkeyword in JavaScript is used to create an object.
Syntax:new fn()
Here,fnrefers to a function or a constructor function, and thenewkeyword must be used with a function; otherwise, it results in an error.But most importantly in this process of object creation, it also sets the
__proto__property of the newly created object.We still don't know what exactly this
__proto__property is. But we learned the fact that it exists by default on every object. We will see what exactly this__proto__is in the next sectionInternal working of
new:
Whennew fn()is executed, there are some predefined sets of activities that it performs. They are:
- Create a brand new object
- Bind
thisto the newly created object. - Set the
__proto__of the newly created object tofn's prototype. - Run the function and return the newly created object (implicit return) if no explicit return is mentioned in
fn.
Let's understand it by an example.
function User(name, age) {
this.name = name;
this.age = age;
}
const u = new User("Peter", 25);
console.log(u);
Now let's try to simulate the work done by the new keyword.
So think of something like below. (Not exactly, but more of a pseudocode)
function User(name, age) {
// const obj = {}; // 1. creation of a new object
// this = obj // 2. binding this to new object created
// this.__proto__ = User.prototype // 3. sets the newly created object __proto__ to User.prototype
this.name = name;
this.age = age;
// return this; // 4. implict return added, new object returned
}
So, as we can see, the
newkeyword plays an important role by not only creating a new object but also setting the created object's__proto__property.We will see the significance of this step in the next section and how this step lays out the foundation for prototypical inheritance
Now I assume there shouldn't be any doubt/confusion in User.prototype. Since User is a function object, it will have access to a property named prototype.
console.log(User.prototype) // {}
Objects in JS:
-
In the previous 2 sections, we learned that every object in JavaScript has access to an inbuilt property named
__proto__and if the object is a function object, then along with__proto__it will also have access toprototype
But what exactly are these predefined properties? What values do these properties hold? What's their significance?
Let's try to answer that. -
In simple terms,
__proto__defines where JavaScript should continue looking when a property is not found on an object itself.
When you try to access something on an object and it doesn’t exist there, JavaScript follows the__proto__link to another object and continues the search.
And that's it; that's the whole purpose of__proto__in JS. -
But since
__proto__is a default, built-in property present in every object, it also has a default value, and it isObject.prototype.
But now what is thisObject.prototype?
Before answering it, let's look at a few examples
const n = new Number(10);
const s = new String('Peter');
const p = new Promise((resolve) => resolve({data: 'success'}));
function User(name, age) {
this.name = name;
this.age = age;
}
const u = new User('Peter', 25);
Exercise: Try to perform all 4 activities done by the
newkeyword.
- Were you able to find any common observation? I think from the above code snippet, the easiest instruction to understand would be const u = new User('Peter', 25), as we clearly discussed this case while we were trying to understand the new keyword
- Here, "User" is a function or, more specifically, a constructor function since it is invoked via the new keyword. Hey, but wait, we also learned that the syntax of the new keyword is new fn(), where fn is a function
- Then how are we able to do new Number(10), new String('Peter'), new Promise((resolve) => resolve({data: 'success'}))? Why does the new keyword work here?
- The idea is actually simple: it works because, under the hood, all of these are constructor functions. In fact, every built-in type you use—Number, String, Promise, Boolean, Array and others—is essentially a predefined constructor function provided by the language itself. And hence the new keyword works.
We can check this by the following:
console.log(typeof Number); // function
console.log(typeof String); // function
console.log(typeof Promise); // function
console.log(typeof User); // function
-
Now the two most important built-in types provided by the language are
ObjectandFunction. And these are constructor functions.
From previous sections, we learned that every function object will have a built-in property namedprototype.
Hence we can do the below
console.log(typeof Object) // function
console.log(typeof Function) // function
console.log(Object.prototype) // {}
console.log(Function.prototype) // {}
Object.prototype and Function.prototype aren't actually empty objects; why we see empty objects is because all the properties of Object.prototype and Function.prototype are non-enumerable, hence the console.log doesn't show them
But we can check the properties of these objects by the following:
console.log(Object.getOwnPropertyDescriptors(Object.prototype)
console.log(Object.getOwnPropertyDescriptors(Function.prototype)
The above instructions give all the properties present in the Object and Function predefined functions.
-
Now let's get back to the default value of
__proto__,. As discussed, the default value isObject.prototype
Meaning, if the property being searched for isn't present in the object, continue the search inObject.prototype, because that's what is present in__proto__. For ex:
const obj = {};
console.log(obj.__proto__ === Object.prototype) // true
console.log(obj.toString()) // '[object Object]'
But wait, in the above code snippet, I never defined any property named toString on my obj, but still somehow we can access the toString property
And what we just discovered is the prototypical inheritance in JS
Let's dissect the flow
When we tried to access
toStringonobj, the JS engine first searchedtoStringon obj. SincetoStringwasn't present inobj, the JS engine checked the__proto__ofobj, it had the value ofObjet.prototype, so now the JS engine continued the search inObect.prototype. InObject.prototype, we find the keytoStringwhose value is a function, and invoking it gave us the result'[object Object]'
NOTE:
What if
toStringdidn’t exist onObject.prototype? The idea is straightforward: JavaScript would continue the lookup using the same mechanism—by following the__proto__chain. It would check the__proto__ofObject.prototypeand keep searching upward.
__proto__property would be present inObject.prototypeas Object.prototype is an object, and every object in JS will have a__proto__propertyThe catch, however, is that
__proto__onObject.prototypeisnull, which signals the end of the prototype chain—there’s nowhere further to look, so the search stops there.
-
So from the above discussion, we understand a couple of things.
1. The default value of__proto__in any object isObject.prototype.
2. The lookup stops if the property isn’t found onObject.prototype, because its__proto__isnull—signaling the end of the prototype chain. Let's take one more example to understand:
const obj = {}
console.log(obj.name) // undefined
Object.prototype.name = 'Parker'
console.log(obj.name) // Parker
- The above example should clear up how the lookup chain works. Let's try to simulate the JS engine.
- obj is an object, and hence it will have a default built-in property named __proto__ with value Object.prototype
- Upon accessing obj.name, search a property named name on obj. If found, return the value; else, continue the search by checking the value of the __proto__ property of obj.
- Since the name property was not found on obj, continue the search in Object.prototype (because that's what is present in obj.__proto__)
- Check for the name property on Object.prototype, if not found, continue the search by checking the value of the __proto__ property inside Object.prototype. The value found is null, meaning stop the lookup chain and return undefined
- But after the first console.log, we are doing something interesting; we are actually adding a property in Object.prototype. So the Object.property should look something like this
- Hence in the 2nd time lookup, we actually get a value of Parker as this time it is found in Object.prototype
-
Now let's discuss the most interesting part. Remember, in the earlier sections, we said that functions are also objects in JS, but they are a special kind of object. But why is that?
We learned a rule that every object in JS will have a__proto__property, and its value will beObject.prototype. But this isn't true in the case of function objects.
For every function object (either custom or predefined),__proto__is equal toFunction.prototype. This can be easily checked as follows.
function wish() {
console.log('Hi');
}
console.log(wish.__proto__ === Function.prototype) // true
console.log(wish.__proto__ === Object.prototype) // false
console.log(Number.__proto__ === Function.prototype) // true
console.log(Promise.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
// the last 2 cases are also true, because Function, Object are also functions in JS. This can be checked using typeof operator
-
Now this should also explain why call, apply, and bind functions can be invoked on every function in JS.
The reason is they exist in Function.prototype
console.log(Object.getOwnPropertyDescriptors(Function.prototype)) // call, apply, bind are properites inside Function.prototype
- Let's understand by taking one more example:
function wish() {
console.log(`${this.name} says Hi`);
}
let obj1 = { name: 'Peter' };
wish.call(obj1);
- Let's again try to simulate the JS engine when wish.call(obj1) is invoked.
- We know the function wish creates an object, and wish is a reference to the object.
- When a property named call is accessed on wish, first the property call is searched in the object referred to by wish. It isn't present, so continue the search by checking the __proto__ of the object referred to by wish.
- Since wish is a function object, its __proto__ has the value Function.prototype, so the lookup is continued in Function.prototype
- In Function.prototype, we find the property call which is a function, and hence the wish.call is executed.
-
The final mental model:
1. If an object then has access to the__proto__property whose value isOjbect.prototype
2. If the object is a function object, then it has access to the__proto__property andprototypeproperty, and the value of__proto__isFunction.prototype.
Observations:
- Since
wish,Function, andObjectare functions (wishbeing custom-defined andFunction,Objectbeing predefined), they have access to both__proto__andprototypeproperties, whereasobjis just a normal object and hence has access to only the__proto__property. - Every function object's
__proto__isFunction.prototypeand normal object's (obj's)__proto__isObject.prototype. - The
prototypeproperty of a function object always has a predefined property namedconstructorwhose value is the reference to the function itself (check the above diagram) -
wish.prototype,Function.prototype, andObject.prototypeare normal objects and not function objects, and hence they have the__proto__value asObject.prototype -
Object.prototype.__proto__isnull—this is defined by the language itself. It marks the end of the prototype chain, which is whyObject.prototypeis considered the topmost level, and property lookup stops here. - This process—where a property is first searched on the object itself, and if not found, the lookup continues along its [[prototype]] (
__proto__property) chain—is precisely why it’s called Prototypical inheritance.
- One final example:
console.log(Function.x)
- Let's become the JS engine again and try to look up the property x
- On the Function object, there is no property named x, so check the __proto__ property of Function and continue the search there. The __proto__ of Function is Function.prototype
- Now let's go to Function.prototype. In Function.prototype also there is no property named x. Check the __proto__ in Function.prototype and go there. The __proto__ of Function.prototype is Object.prototype
- Now check the Object.prototype. There is no property named x in Object.prototype, so check the __proto__ of Object.prototype. It's null. So stop the lookup and return undefined
(Check the above diagram for any confusions.)
Inheritance chain (instance and static):
The previous section could be heavy, but once that is cleared, there is not much left to understand in Prototypical inheritance.
In this section, let's see how to implement inheritance in JavaScript (via [[prototype]])
In the new keyword section, we saw that new keywords set the newly created object's [[prototype]] (__proto__ property) to the constructor function's prototype
Ex:
function RateLimiter(limit) {
this.limit = limit;
this.requests = 0;
}
let user1RateLimiter = new RateLimiter(5);
let user2RateLimiter = new RateLimiter(10);
Now user1RateLimiter and user2RateLimiter are two objects from the RateLimiter constructor function
Exercise: Try to run the 4 activities done by the new keyword
- In the memory, 2 objects are created, and each object will have its own copy of
limitandrequests, because thenewkeyword creates a new object and returns it (implicit return). This can be checked by following
user1RateLimiter.limit++;
user2RateLimiter.limit++;
console.log(user1RateLimiter.limit) // 6
console.log(user2RateLimiter.limit) // 11
-
Now what if I need to add a method named
isRequestAllowedto check whether the current request should be allowed or not? Basically, we need to check whether the current request number is less than the defined limit or not.
For this, we need to define a method, but where should we define that method? The only condition is this method should be available to all the instances created out of the constructor function. -
What if we define the method
isRequestAllowedin theRateLimiterfunction object itself? Will this suffice for our requirement?
Check the following:
-
Clearly this doesn't work, because when we try to access the method
isRequestAllowedon the instanceuser1RateLimiter(run the algorithm we learned in the previous section), we getundefined.
Because we first search the propertyisRequestAllowedon theuser1RateLimiterobject, since it's not found, we check the__proto__which has the valueRateLimiter.prototype(thenewkeyword sets this), and even inRateLimiter.prototypethere is no property namedisRequestAllowed, propagating the search toRateLimiter.protoytpe.__protowhich points toObject.prototype. SinceObject.prototypealso doesn't have any property namedisRequestAllowed, the search propagates toObject.prototype.__proto__whose value isnull, hence leading toundefined. So defining the method on the function object itself didn't help us. The correct place to put these methods would be
RateLimiter.prototype, the reason being all the objects created from the constructor functionRateLimterwill have the same__proto__i.e,RateLimiter.prototype, and hence defining the method inRateLimiter.prototypewill make sure that each instance created fromRateLimitercan access the method.
console.log(user1RateLimiter.__proto__ === user1RateLimiter.__proto__);
// true, because both point to RateLimiter.prototype because of new keyword
console.log(user1RateLimiter.isRequestAllowed === user2RateLimiter.isRequestAllowed) // true, because it's the same method both objects reach in chain
- Now let's extend the same concept to achieve Parent-Child relationship in JavaScript. Consider the following example:
function Notification() {}
Notification.prototype.formatMessage = function (msg) {
return `[Notification]: ${msg}`;
}
function EmailNotification() {}
EmailNotification.prototype.send = function (msg) {
return `Email sent with message: ${msg}`;
};
const emailNotification = new EmailNotification();
console.log(emailNotification.send("Hello")); // Email sent with message: Hello
console.log(emailNotification.formatMessage("Hey there!")); // undefined
- It would be clear why the 2nd console.log gives undefined, simply because the formatMessage property was never found in the prototype chain (EmailNotification.prototype)
- But we want the formatMessage method defined on the Notification function prototype to be available on the emailNotification instance
- Meaning we want to establish a relationship between the EmailNotification function and the Notification function, so when a property isn't found on Notification.prototype, the current next searched place is Object.prototype, because Notifcation.prototype.__proto__ is Object.prototype
- Current Relationship:
- To reach the target, we just need to do a minor tweak to EmailNotification.prototype. We want the chain to look like emailNotification -> EmailNotification.prototype -> Notification.prototype -> Object.prototype -> null
- The magic line that will allow us to do this is:
EmailNotification.prototype = Object.create(Notification.prototype)
By default, every object (except function objects) has its
__proto__pointing toObject.prototype. However, if you want to create an object with a custom prototype,Object.create()allows you to do exactly that by explicitly setting the object’s__proto__.
- So Object.create(Notification.prototype) will create a new object whose __proto__ would be equal to Notification.prototype. And this new object's reference is assigned to EmailNotification.prototype
- The only downside is, as discussed in the previous section, the prototype object present in the function will always have a default key named constructor in it. But due to Object.create(Notification.prototype), we created an entirely new object, hence lost the constructor key, so as a good practice, let's add it back
EmailNotification.prototype = Object.create(Notification.prototype)
EmailNotification.prototype.constructor = EmailNotification
- And that's it; the above 2 lines will help us create the parent-child relationship (inheritance) in JavaScript
The inheritance we observed above can be described as instance-based inheritance, since the lookup chain is followed through the instance’s prototype (
__proto__).-
Now let's understand static inheritance.
Strictly speaking, JavaScript didn’t have an explicit concept of static inheritance before ES6. With the introduction of classes in ES6, thestatickeyword formalized this idea, enabling inheritance of static methods and properties across classes.
The idea is simple: instance inheritance works by looking up the [[Prototype]] (__proto__) chain starting from the instance created by the constructor function.
In contrast, static inheritance works by following the [[Prototype]] (__proto__) chain on the function itself (function object)—rather than on its instances. Let's take an example to understand:
function Animal() {}
Animal.identify = function () {
return "I am an animal";
};
- If observed closely, we aren't defining the identify method in the Animal.prototype object; rather, we are defining this method on the Animal function object itself, and these are what are considered static methods in ES6.
- Now let's define the child class:
function Cat() {}
console.log(Cat.identify); // undefined
console.log(Cat.identify()); // cannot invoke function on undefined
- I hope at this point, we are clear why Cat.idenify returns undefined. Because the property identify was not found in the function Cat [[prototype]] chain (__proto__ chain). Search order: Cat -> Cat.__proto__ = Function.prototype -> Function.prototype.__proto__ = Object.prototype -> null
- Now what we want is property identify to be available to the function Cat, meaning we have to tinker with the __proto__ chain such that, once not found in Cat, the next search should happen in Animal (and not in Function.prototype).
- The magic line that will allow us to achieve this is:
Cat.__proto__ = Animal
console.log(Cat.identify); // [Function]
conosole.log(Cat.identify()); // I am an animal
- By doing the above, we are manipulating the __proto__ chain of Cat to search next in the Animal function object
- And what we witnessed above is called static inheritance in JavaScript.
ES6 Updates:
Awesome that you have reached until here; what we learned in the previous sections officially completes the prototypical inheritance in JavaScript.
But ES6 released a set of new features that allows us to establish inheritance in an easier way.
New keywords introduced in ES6 regarding prototypical inheritance: class, static, extends, and super.
1. The class keyword:
Strictly speaking, the
classkeyword is just syntactic sugar for constructor functions.This was mainly for the developers who came from other ecosystems like Java, C#, etc. But under the hood it's the same constructor function.
Example:
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
wish() {
console.log(`${this.name} says Hi`);
}
static isValidAge(age) {
return age >= 0 && age <= 120;
}
}
const u = new User('Peter', 25);
console.log(u.name); // Peter
console.log(u.age); // 25
user.wish(); // Peter says Hi
User.isValidAge(18); // true
Under the hood, the above class becomes:
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.wish = function() {
console.log(`${this.name} says Hi`);
}
User.isValidAge = function(age) {
return age >= 0 && age <= 120;
}
- i) The code written inside a class constructor essentially becomes the body of the underlying constructor function.
- ii) The instance method defined in the class (wish) is added to the User.prototype (so that instances can have access to this method)
- iii) The static method defined in the class (defined via the static keyword) is added directly as a property on the constructor function (User function object).
- The
newkeyword works the same even for classes. The same 4 activities will be performed by thenewkeyword in the background.
- Classes in JavaScript, by default, will run in strict mode. And can be invoked only via the
newkeyword.
2. The extends keyword:
The extends keyword is used to establish the parent-child relationship between classes.
Example:
class Animal {
speak() {
return "generic animal sound";
}
static describe() {
return "Base class for all animals";
}
}
class Cat extends Animal {
meow() {
return "Meow!";
}
static identify() {
return "Cat class";
}
}
const cat = new Cat();
console.log(cat.meow()); // Meow!
console.log(Cat.identify()); // Cat class
console.log(cat.speak()); // generic animal sound
console.log(cat.describe()); // Base class for all animals
- As we can see, the child class instance (
cat) can access properties and methods defined in the parent class (Animal). This is made possible by theextendskeyword, which establishes the inheritance relationship between them.
But what it does under the hood is what we do manually in the case of constructor functions.
// set instance inheritance
Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat
// set static inheritance
Object.setPrototypeOf(Cat, Animal)
We don't need to do the above manually; extends does it on behalf of us.
3. The super keyword:
-
The
superkeyword allows us to call the parent class constructor function to perform the initialization of the child object.
Yes, you heard it right—callingsuper()simply initializes the child class instance using the parent’s constructor; it does not create a separate parent class object. This might sound surprising, especially to the devs coming from a Java background, where invoking
super()first creates the parent class object, but that isn't true in the case of JavaScriptBecause remember, all the new syntax that came in ES6 is just syntactic sugar for the traditional construction functions. Invoking
super()simply means calling the parent constructor function by passing the child'sthis.Let's understand it by an example:
class Animal {
constructor(name) {
this.name = name;
console.log("Animal constructor called");
}
}
class Cat extends Animal {
constructor(name, color) {
super(name); // calls parent constructor on the SAME object
this.color = color;
console.log("Cat constructor called");
}
}
const cat = new Cat('Yapapa cat', 'gold');
console.log(cat.name);
console.log(cat.color);
Output:
Animal constructor called
Cat constructor called
Yapapa cat
gold
- Under the hood, the above classes convert to the below constructor functions
function Animal(name) {
this.name = name;
console.log("Animal constructor called");
}
function Cat(name, color) {
Animal.call(this, name); // important: Calling Animal function with Cat's this
this.color = color;
console.log("Cat constructor called");
}
// setting both instance and static inheritance between Animal and Cat
Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat;
Object.setPrototypeOf(Cat, Animal);
const cat = new Cat('Yapapa cat', 'gold');
console.log(cat.name);
console.log(cat.color);
Now we know that because of the
newkeyword (new Cat('Yapapa cat','gold')), a new object would be created, andthisbinds to the new object created, and the same object is passed inAnimal.call, and hence on the cat instance, thenameproperty is added (by theAnimalconstructor function).-
A natural question that can arise is, why even initialize the properties in the parent class, as the instance is always of the child class, and why not directly perform the initialization of the instance in the child constructor function itself?
The answer is simple: To not violate the DRY principle, the main purpose of creating a parent class/parent constructor function is to keep the common code together
A parent class/constructor function can have many child classes/constructor functions, so instead of initializing all the properties in the child class constructor itself, a better practice is to perform the initialization of common properties in the parent class/constructor function so that we don't have to repeat the initialization logic in each child class/constructor function
Example:
class Vehicle {
constructor(brand, year) {
this.brand = brand;
this.year = year;
}
}
class Bike extends Vehicle {
constructor(brand, year, type) {
super(brand, year); // common initialization
this.type = type;
}
}
class Car extends Vehicle {
constructor(brand, year, doors) {
super(brand, year); // common initialization
this.doors = doors;
}
}
As we could, we extracted out the common initialization logic to the parent class, saving ourselves from repeating, and super in ES6 is a cleaner way to achieve this instead of Car.call(this, brand, year)
This ends the post. I hope this post added value to your JS learning journey. If yes, please do leave a comment/reaction.








Top comments (0)