How to Extend Multiple Classes in JavaScript
- Composition Approach to Extend Multiple Classes in JavaScript
- Behavioral Delegation Approach to Extend Multiple Classes in JavaScript
JavaScript does not have a built-in mechanism for extending multiple classes. However, to achieve a similar effect of multiple inheritances in JavaScript, use techniques called composition
and behavioral delegation
.
Composition Approach to Extend Multiple Classes in JavaScript
With composition, instead of inheriting properties and methods from multiple classes, you can create an instance of each class, store it as a new class property, and then delegate the methods calls to the corresponding instances.
It’s important to note that with this approach, you have more control over the class’s behavior; you can access the properties and methods of the composed class and override methods if needed.
Implementation to Extend Multiple Classes
In the following example, class C has an instance of class A and class B as properties, and it delegates the method calls to the corresponding instances. This way, class C can access the methods of both class A and class B.
Class A has a single method called methodA
that outputs a message Class A: methodA called
to the console when called. Class B has a single method called methodB
that outputs a message Class B: methodB called
to the console when called.
class A {
methodA() {
console.log('Class A: methodA called');
}
}
class B {
methodB() {
console.log('Class B: methodB called');
}
}
Class C uses the composition pattern to achieve a similar effect of multiple inheritances. The constructor creates instances of class A and class B and stores them as properties a
and b
, respectively.
constructor() {
this.a = new A();
this.b = new B();
}
Class C has two methods, methodA
and methodB
, which perform additional operations before and after calling the corresponding methods of classes A and B.
methodA() {
console.log('Class C: Additional operation before calling methodA');
this.a.methodA();
console.log('Class C: Additional operation after calling methodA');
}
methodB() {
console.log('Class C: Additional operation before calling methodB');
this.b.methodB();
console.log('Class C: Additional operation after calling methodB');
}
When the last two lines are executed, they create an instance of class C and then call its methodA()
and methodB().
const c = new C();
c.methodA();
c.methodB();
Source Code:
class A {
methodA() {
console.log('Class A: methodA called');
}
}
class B {
methodB() {
console.log('Class B: methodB called');
}
}
class C {
constructor() {
this.a = new A();
this.b = new B();
}
methodA() {
console.log("Class C: Additional operation before calling methodA");
this.a.methodA();
console.log("Class C: Additional operation after calling methodA");
}
methodB() {
console.log("Class C: Additional operation before calling methodB");
this.b.methodB();
console.log("Class C: Additional operation after calling methodB");
}
}
const c = new C();
c.methodA();
c.methodB();
Output:
Behavioral Delegation Approach to Extend Multiple Classes in JavaScript
Another variation of this approach is to use behavioral delegation
where instead of storing instances of the composed class, you directly delegate the method calls to a property on the composed class.
Implementation to Extend Multiple Classes
When the methodA
of class C is called, it first outputs a message Class C: Additional operation before calling methodA
to the console, then it calls the methodA
of class A by using A.prototype.methodA.call(this);
, this will output Class A: methodA called
to the console.
Finally, it outputs a message Class C: Additional operation after calling methodA
to the console.
Similarly, when the methodB
of class C is called, it first outputs a message Class C: Additional operation before calling methodB
to the console, then it calls the methodB
of class B by using B.prototype.methodB.call(this);
, this will output Class B: methodB called
to the console.
Finally, it outputs a message Class C: Additional operation after calling methodB
to the console.
This way, class C can access the methods of both class A and class B and add some additional operations to the methods without modifying the original classes.
It is important to notice that, in this case, class C doesn’t have instances of A and B. Instead, it uses the call
method to invoke the methods of A and B in the context of class C.
Source Code:
class A {
methodA() {
console.log('Class A: methodA called');
}
}
class B {
methodB() {
console.log('Class B: methodB called');
}
}
class C {
methodA() {
console.log("Class C: Additional operation before calling methodA");
A.prototype.methodA.call(this);
console.log("Class C: Additional operation after calling methodA");
}
methodB() {
console.log("Class C: Additional operation before calling methodB");
B.prototype.methodB.call(this);
console.log("Class C: Additional operation after calling methodB");
}
}
const c = new C();
c.methodA();
c.methodB();
Output:
This approach is more memory efficient than composition but less flexible because it can’t access the properties of the composed class.
As you can see, in both cases, class C can add some additional operations to the methods of class A and class B without modifying the original classes. This way, you can keep the original classes clean and separated and added specific functionality to the new class C.
I am Waqar having 5+ years of software engineering experience. I have been in the industry as a javascript web and mobile developer for 3 years working with multiple frameworks such as nodejs, react js, react native, Ionic, and angular js. After which I Switched to flutter mobile development. I have 2 years of experience building android and ios apps with flutter. For the backend, I have experience with rest APIs, Aws, and firebase. I have also written articles related to problem-solving and best practices in C, C++, Javascript, C#, and power shell.
LinkedIn