Lesson 9 - Inheritance And Polymorphism In JavaScript

In the previous lesson, Finishing an Object-Oriented Diary In JavaScript, we finished our object-oriented diary. In today's tutorial, we're going to look at inheritance and polymorphism in JavaScript.

Inheritance

Inheritance is one of the basic features of OOP and is used to create new data structures based on old ones. Let's show it on an example. We'll create a common Human class and assign name and age properties to it. We'll also add a greet() method:

class Human {

    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        return `My name is ${this.name} and I'm ${this.age}.`;
    }
}

The code shouldn't surprise you.

Extending the Class

Let's say we can do it with this class for a while. But what if we need people who have some extra features? E.g. a programmer is also a person with a name, age, and the ability to greet, but he should have an extra property for the programming language he uses.

A Naive Solution

It may cross your mind to create a Programmer class, copy the code from the Human class there, and just add the language property and, for example, a program() method:

class Programmer {

    constructor(name, age, language) {
        this.name = name;
        this.age = age;
        this.language = language;
    }

    greet() {
        return `My name is ${this.name} and I'm ${this.age}.`;
    }

    program() {
        return `I'm programming in ${this.language}...`;
    }

}

While this solution is functional, it violates one of the most fundamental good practices of all programmers - the DRY principle (Don't Repeat Yourself). Imagine that in the future you will decide to edit the Human class and forget that it's copied to several other files as well. Such errors are then hard to find, and of course, your application will become broken. So this is definitely not the way we're going to extend our classes :)

A Solution Using Inheritance

We'll actually create the new Programmer class, but we'll inherit it from the Human class. In JavaScript, we use the extends keyword in the definition of the new class to inherit it from another, see below:

class Child extends Parent

Inheritance And Constructors

But there's a small catch. Once we need a constructor in the child class, we must first call the parent's constructor by the super() function and pass it the necessary parameters if needed. We must call the parent constructor before using the this keyword. That makes sense since the constructor prepares the object for use, and if the parent has a constructor, the child should call it in its constructor to initialize everything. We must call the parent constructor in the child's constructor even if the parent didn't have its own constructor defined (even in this case it actually would have a constructor which is generated automatically for all objects if the custom one isn't provided).

So let's inherit a new Programmer class from our Human class:

class Programmer extends Human {

    constructor(name, age, language) {
        super(name, age);
        this.language = language;
    }

    program() {
        return `I'm programming in ${this.language}...`;
    }
}

As we can see, we have a three-parameter constructor here. The name and age parameters are passed by super() to the constructor of the Human class. Then, we can see the language property, which we create here in the programmer's constructor and assign the value to it from the parameter of the same name. The Programmer class has three properties now: name, age, and language, and two methods, the inherited greet() method and program().

Now we'll create a Programmer class instance in the main script and try to call both methods:

const programmer = new Programmer("Simon", 19, "JS");
document.write(`
${programmer.greet()}<br>
${programmer.program()}<br>
`);

Now we can look at the result:

Your page
localhost

Benefits of Inheritance

The advantages of using inheritance are clear. We don't have to copy the same properties to two classes. All we have to do is declare new ones. The rest will be inherited. The benefits are tremendous, we can extend existing components of new methods and reuse them. We don't have to write lots of redundant code, and most importantly - when we change a single property in the parent class, this change is then inherited everywhere automatically. We don't need to change something manually in 20 classes, which could potentially cause errors. We're humans and we'll always make mistakes, we have to choose the right programming techniques to make as less mistakes as possible.

Languages can support either single or "multiple inheritance". With the single inheritance, a class can inherit only from one other class. With the "multiple inheritance", a class can inherit from several classes at the same time. Multiple inheritance never became very popular. JavaScript only supports the single inheritance. You can encounter multiple inheritance in the C++ language. Of course, with the single inheritance, a child class can have another child as well.

Polymorphism

Don't be scared of the obscure name of this technique, it's actually very simple. Polymorphism allows us to use a unified interface to work with objects of different types. Imagine that we have, for example, many objects representing geometric shapes (circles, squares, triangles, and so on). It'd certainly be helpful if we could communicate with them in some unified way even though they're different. We can create a GeometricShape class containing a color property and a render() method. All the geometric shapes would then inherit the interface from this class. Now you may be thinking, "But the circle and square object would render differently!." Well, polymorphism allows us to override the render() method in every subclass so it will do what we want. The interface will be unified and we won't have to think about which method to call to render different objects.

Polymorphism is often explained using animals. All having a speak() method in their interface, but each animal performs it differently.

Polymorphism

The essence of polymorphism is a method or methods, that all the descendants have defined with the same heads, but with different method bodies. Let's try it on our people.

Greetings

Our human has the greet() method defined, which greets this way:

My name is Mark and I'm 26

Overriding the method

Now let's apply the polymorphism by modifying the greet() method of the ancestor in the Programmer class. Overriding methods in JavaScript is simple - we just redefine them. So let's teach the programmer to greet in some geek way, such as using the greeting "Hello world!":

class Programmer extends Human {

    constructor(name, age, language) {
        super(name, age);
        this.language = language;
    }

    program() {
        return `I'm programming in ${this.language}...`;
    }

    greet() {
        return `Hello world! My name is ${this.name} and I'm ${this.age}.`;
    }
}

The result:

Your page
localhost

If you call the greet() method on a Human instance, it'll print the original greeting text. You can try it :) So we got the same interface and different functionality which is chosen according to the particular object.

Improvements

Note that we used the basic greeting from the Human class, we just added "Hello world!" to it. Our solution above is not ideal, since for more types of people we'd always have to copy the same default greeting to the new class and add the new text before it. And we already know that according to DRY, any copying of the same code is wrong.

The basic greeting can be returned by the greet() method of the Human class, and we can call it from the children as well as we called the Human class constructor - by the super() keyword. Then we'll just simply add the new text to it and return the result:

class Programmer extends Human {

    constructor(name, age, language) {
        super(name, age);
        this.language = language;
    }

    program() {
        return `I'm programming in ${this.language}...`;
    }

    greet() {
        return `Hello world! ${super.greet()}`;
    }
}

Let's try to create a Human instance in the main script and call the greet() method on it to see the difference:

const programmer = new Programmer("Simon", 19, "JS");
document.write(`
${programmer.greet()}<br>
${programmer.program()}<br>
`);

const human = new Human("Mark", 26);
document.write(human.greet());

The output will look like this:

Your page
localhost

So that's all for today :) The source codes for download are available below. Next time, in the lesson Object Properties in JavaScript, we'll look at properties in JavaScript.


 

 

Activities (2)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!