Extending Class without `extends`

RohitRohit
7 min read

This is a follow-up blog to my recent one on How Everything in JS is an Object.
I have already discussed what classes are and how they work under the hood in JavaScript.
If you haven't checked it out, feel free to do so, as it will help you understand this blog better.

In this post, we will look into an interesting question: How can we extend a class without using the extends keyword?

Before diving into this question, let’s first have a fundamental understanding of what class and extends are.

Class and Extends

The concept of a class primarily comes from the Object-Oriented Programming (OOP) paradigm. What is OOP? Let me briefly explain that too.

OOP (Object-Oriented Programming)

OOP is a programming paradigm — a way of writing code where you relate items to the real world. It is the core behind some languages like Java and C++.

In OOP, everything is an object. An object is derived from real-world entities that have properties and functions. For instance, a car has properties like color, model, and functionalities like accelerate, brake, etc. Hence, a car is considered an object.

The Class here acts as a blueprint for the car object. Now, what does that mean?

Well, let’s take the example of the Thar from Mahindra. If you look at any Thar of the same model, you would find a common structure and build (unless modified).

So, there might be some guidelines and rules while manufacturing the Thar that ensure it has a common structure and build, right?

Well, in OOP, these rules and guidelines that decide the properties and functionalities of objects are called Classes. So objects are made from classes, and classes define how objects are created and what they should contain.

Now that we understand classes and objects, let’s briefly look at the four pillars of OOP.

Four Pillars of OOP

The four main pillars are Polymorphism, Inheritance, Abstraction, and Encapsulation.

While not all of these pillars are necessary for this blog, let me give you a brief idea of what they mean:

  • Polymorphism: Polymorphism refers to a single function taking multiple forms for different kinds of objects. For example, a speak() function can produce different outputs depending on whether it's called on a Dog or a Cat.

  • Abstraction: Abstraction refers to hiding the internal implementation of code. For instance, using Math.min() in JS. You know what it does, but the internal implementation is hidden.

  • Encapsulation: Encapsulation refers to wrapping code into a single function while restricting access to certain parts of it. For example, setPrototypeOf and getPrototypeOf are used to restrict direct access to the [[Prototype]] property on objects.

  • Inheritance: Inheritance refers to inheriting properties and functionality from a parent class. For example, a Dog class being inherited from an Animal class.

You can look up these concepts in more detail, but for now, we will focus on the Inheritance part.

Extends = Inheritance

Yes, the extends keyword represents the concept of inheritance in OOP. It helps inherit the properties and functionality of a parent class. But, you may ask: Why do we need the properties of one class in another? What is the use of it?

Let’s understand this through a real-world example.

Let us continue with an example of Thar.

The Thar is one of the vehicles Mahindra manufactures, but there are other vehicles too, right?
Hence, we can make a base class (parent class) and put all the common properties, like the brand name Mahindra, and functionalities like brake, accelerate, etc., there.

Now for every other vehicle manufactured by Mahindra you can extend this class MahindraVehicles and hence you don’t have to define the brand name in each and every of them.

class MahindraVehicles {
    constructor(brand) {
        this.brand = brand;
    }

    accelerate() {
        // accelerate functionality here
    }
}

class Thar extends MahindraVehicles {
    // Some functionality here
    constructor(model, brand) {
        super(brand)
        this.model = model;
    }
}

class Scorpio extends MahindraVehicles {
    // Additional functionality for Scorpio
    constructor(model, brand) {
        super(brand)
        this.model = model;
    }

    offRoad() {
        console.log("Scorpio is excellent for off-road adventures!");
    }
}

You see two major things here: the super keyword and the constructor function. Let’s understand them.

Understanding Super keyword

Any word that is reserved by the programming language itself is known as a keyword. Super is a major keyword that enables inheritance. It’s also present in languages like Java and is a significant feature of inheritance in the OOP paradigm.

Purpose: The super keyword allows the child class to pass its properties to the parent class. It is particularly useful when we want to initialize the constructor properties of the parent class directly from the child class.

Now let's understand the constructor functions.

Constructor Function and Classes

A constructor function in a class is a function that is automatically called whenever a new object is created from the class. The constructor keyword is used to make the constructor function in a class, as shown in the above code.

If you want to initialize the properties of the parent constructor function from child class itself, you would use the super keyword.

class MahindraVehicles {
    constructor(name, brand) {
        this.name = name;
        this.brand = brand;
    }
}

class Thar extends MahindraVehicles {
    // Some functionality here
    constructor(name, brand, model) {
        super(name, brand)
        this.model = model
    }
}

Classes Before ES6

Before the ES6 version of JavaScript, there was no concept of classes. To achieve Inheritance, we used the native prototypal Inheritance (explained in my previous blog). However, with the ES6 version, JavaScript introduced the concept of Class, as OOP became quite popular.

Since classes are part of OOP, JS also introduced the extends keyword to maintain familiarity with OOP among programmers.

Under the hood, these classes and the extends keyword are using functions only, so they are called syntactic sugar. To extend a class without the extends keyword, it’s necessary to understand a few things:

  • JavaScript follows prototypal inheritance, meaning every object in JS has a [[Prototype]] property that points to another object.

  • This prototype chain ends when the last [[Prototype]] points to Object, hence everything in JS is an object.

  • It ends there because the [[Prototype]] of Object points to null.

All of these concepts are explained in detail in my previous blog.

Extending a Class without extends

Now that we understand the basic concepts, the constructor function, and the super keyword, let’s try answering the initial question: How can we extend a class without using the extends keyword?

We will be utilizing the same concept of prototypal inheritance. As discussed, every object in JS has a [[Prototype]] property attached to it internally. In the case of classes, this property resides under the .prototype property, along with the constructor.

Hence, to give one class the methods and properties of another, we just need to add the parent class to the [[Prototype]] property of the child class. Let’s see that in action:

Now, you can access all the methods present in MahindraVehicles, like accelerate().

So what’s the problem here ? The problem is you cannot use or access the name and brand of the parent class as you haven’t declared/ constructed it yet. You cannot do the following thing without using the super keyword and passing the name and brand.

But the catch here is you cannot use super keyword without using the extends keyword on a class. So can’t we mimic the super keyword ? You can but you must go to the internal Constructor function to mimic the whole class functioning.

Mimicking super without super keyword

Now you may wonder, Can we mimic the super keyword too? Well, technically you can. But to mimic the super keyword, you must make the constructor function itself.

The MahindraVehicles.call() mimics the use of super keyword here.

Can’t you mimic super in the class itself?

  • No, because you cannot call the constructor functions for classes without creating objects using .new. Hence, we were given the super keyword to simplify this.

Conclusion

In this blog, we’ve explored an essential concept of classes and their inner workings in JavaScript, while also diving into the principles of OOP. We’ve learned how the extends keyword and classes as a whole acts as syntactic sugar, how to mimic the super keyword, and what role it plays in inheritance, all explained with practical examples.


If you found this blog helpful, feel free to like it and subscribe to my newsletter. You’ll receive more insights like this, delivered in an easy-to-understand format! Stay tuned, as we will soon be diving deeper into JavaScript internals.

0
Subscribe to my newsletter

Read articles from Rohit directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rohit
Rohit

Just a curious child exploring tech