Lesson 10 - 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:
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.
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:
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:
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.