Inheritance is a core concept in object-oriented programming (OOP) that allows one class to inherit properties and methods from another. In JavaScript, inheritance lets developers create new classes that build upon existing ones, promoting code reuse and logical structure. When a class inherits from another, it can access and use the parent’s features while also adding or changing its own behavior. This concept closely mirrors real-world relationships, such as how a dog is an animal but has its own unique traits.
JavaScript supports inheritance through its prototype system, but modern syntax uses class
and extends
keywords to express inheritance clearly and simply. This article will guide you through the process of using inheritance in JavaScript, focusing on practical “how-to” steps with engaging, runnable examples that will make the concept clear and fun to apply.
Creating a Base Class
To begin understanding inheritance, we first need a base class. This class acts like a blueprint for other classes that will extend it. Consider an Animal
class which will have basic properties and methods that all animals share.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
Here, the Animal
class has a constructor that accepts a name
and a method speak
that outputs a simple message. This class can now be used as a foundation to create more specific animal types.
By running this code, you have a class with one property (name
) and one behavior (speak
). Any class that inherits from Animal
will automatically have these features unless changed.
Extending a Class Using extends
Inheritance happens when a new class extends an existing class. This means the new class will inherit all properties and methods from the original. For example, let’s create a Dog
class that inherits from Animal
.
class Dog extends Animal {}
const myDog = new Dog('Kol');
myDog.speak();
In this code, Dog
extends Animal
but adds nothing new yet. When we create a Dog
object named “Kol” and call speak()
, it uses the inherited method from Animal
, printing “Kol makes a sound.”
This simple extension shows that Dog
now behaves like an Animal
, having all its properties and methods without any extra code.
Calling the Parent Class Constructor with super()
When you extend a class, the subclass needs to call the parent class’s constructor to properly initialize inherited properties. This is done using super()
inside the subclass constructor.
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
const myDog = new Dog('Kol', 'Beagle');
console.log(`${myDog.name} is a ${myDog.breed}.`);
Here, the Dog
constructor takes name
and breed
. It uses super(name)
to call the Animal
constructor, setting the name
. Then, it sets the breed
property unique to Dog
.
Running this code will output: “Kol is a Beagle.” This shows how inheritance combines shared properties from the parent with subclass-specific properties.
Adding New Properties and Methods in Subclass
Subclasses can add their own properties and methods on top of what they inherit. Let’s add a new method bark()
to Dog
.
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
console.log(`${this.name} barks loudly!`);
}
}
const myDog = new Dog('Kol', 'Beagle');
myDog.speak();
myDog.bark();
This example shows Dog
having both inherited method speak()
and new method bark()
. The bark()
method prints a dog-specific message. This demonstrates how subclass objects combine inherited and unique features seamlessly.
Overriding Parent Methods in Subclass
Subclasses can override methods they inherit to provide their own behavior. This means defining a method with the same name as the parent but with a different implementation.
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} says: Woof!`);
}
}
const myDog = new Dog('Kol', 'Beagle');
myDog.speak();
Here, Dog
overrides the speak()
method to print a more dog-like message. When calling myDog.speak()
, the overridden method runs instead of the parent’s. This allows subclasses to specialize inherited behavior.
Using super
to Call Parent Methods from Subclass
Sometimes you want to override a method but still use the parent’s original behavior. You can do this by calling super.methodName()
inside the overridden method.
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
super.speak();
console.log(`${this.name} also says: Woof!`);
}
}
const myDog = new Dog('Kol', 'Beagle');
myDog.speak();
In this code, Dog
’s speak()
first calls Animal
’s speak()
using super.speak()
, then adds an extra line. It shows how you can combine parent and child behavior in one method.
Multiple Levels of Inheritance
Inheritance can be chained through multiple levels. For example, ServiceDog
can inherit from Dog
, which inherits from Animal
.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
console.log(`${this.name} barks loudly!`);
}
}
class ServiceDog extends Dog {
constructor(name, breed, job) {
super(name, breed);
this.job = job;
}
performJob() {
console.log(`${this.name} is performing the job: ${this.job}`);
}
}
const myServiceDog = new ServiceDog('Max', 'Labrador', 'guide dog');
myServiceDog.speak();
myServiceDog.bark();
myServiceDog.performJob();
Here, ServiceDog
adds a job
property and performJob()
method. It inherits speak()
from Dog
, which inherits from Animal
. This shows how inheritance flows through multiple generations of classes.
Fun Example: Magical Creature Inheritance
To make it more exciting, let’s build a magical creature hierarchy: Creature
→ Wizard
→ FireWizard
.
class Creature {
constructor(name) {
this.name = name;
}
greet() {
console.log(`${this.name} says hello.`);
}
}
class Wizard extends Creature {
constructor(name, magicType) {
super(name);
this.magicType = magicType;
}
castSpell() {
console.log(`${this.name} casts a ${this.magicType} spell.`);
}
}
class FireWizard extends Wizard {
castSpell() {
super.castSpell();
console.log(`${this.name} casts a blazing fireball!`);
}
}
const harry = new FireWizard('Harry', 'fire');
harry.greet();
harry.castSpell();
This example builds three levels of inheritance. FireWizard
overrides castSpell()
but calls the parent version first. It’s a vivid example of inheritance in action, with fun magical storytelling.
Conclusion
Inheritance in JavaScript lets you create class hierarchies that share and extend behavior. Using extends
and super()
, subclasses can inherit properties and methods, add their own, override parent methods, and even call the original parent method. This structure helps keep code organized and expressive, just like natural relationships in the world.
By understanding and practicing inheritance, you gain a powerful tool to build complex and reusable JavaScript programs.
References
If you want to learn more about JavaScript inheritance and related topics, these resources are excellent starting points:
- MDN Web Docs: Classes
The official documentation explaining class syntax and usage. - MDN Web Docs: Inheritance and the prototype chain
Learn how classes work with JavaScript’s prototype system. - MDN Web Docs:
super
Understand howsuper
works in constructors and methods.