📖 Inheritance in JavaScript
Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows one class to inherit properties and methods from another. This promotes code reuse, reduces redundancy, and helps to create a more organized and scalable codebase. In JavaScript, inheritance is implemented using the extends
and super
keywords.
What is Inheritance?
Inheritance allows a class (known as a subclass or child class) to inherit the properties and methods of another class (known as a superclass or parent class). This means that the child class can use the functionality of the parent class without having to rewrite the code. Additionally, the child class can have its own properties and methods, or it can override the inherited ones.
Using the extends
Keyword
The extends
keyword is used to create a subclass that inherits from a parent class. When a class extends another class, it inherits all of the parent class's properties and methods.
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, my name is ${this.name}`;
}
}
class Student extends Person {
constructor(name, studentID) {
super(name); // Calls the parent class's constructor
this.studentID = studentID;
}
getDetails() {
return `${this.name} (ID: ${this.studentID})`;
}
}
const student = new Student('Jane Doe', 'S67890');
console.log(student.greet()); // Outputs: Hello, my name is Jane Doe
console.log(student.getDetails()); // Outputs: Jane Doe (ID: S67890)
In this example, the Student
class extends the Person
class. This means that Student
inherits the greet
method from Person
. The Student
class also adds a new method, getDetails
, and a new property, studentID
.
Using the super
Keyword
The super
keyword is used to call the constructor
of the parent class. This is necessary when a subclass has its own constructor but still needs to initialize properties inherited from the parent class.
class Person {
constructor(name) {
this.name = name;
}
}
class Student extends Person {
constructor(name, studentID) {
super(name); // Calls the constructor of Person
this.studentID = studentID;
}
}
const student = new Student('John Doe', 'S12345');
console.log(student.name); // Outputs: John Doe
console.log(student.studentID); // Outputs: S12345
Here, super(name)
in the Student
constructor calls the constructor of the Person
class, passing the name
parameter to it. This ensures that the name
property is correctly initialized by the parent class before adding additional properties like studentID
in the child class.
Overriding Methods in the Subclass
One of the key benefits of inheritance is that subclasses can override methods inherited from the parent class. This allows subclasses to provide specific implementations for methods that are more appropriate for their context.
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, my name is ${this.name}`;
}
}
class Student extends Person {
constructor(name, studentID) {
super(name);
this.studentID = studentID;
}
greet() {
return `${super.greet()} and my student ID is ${this.studentID}`;
}
}
const student = new Student('Jane Doe', 'S67890');
console.log(student.greet()); // Outputs: Hello, my name is Jane Doe and my student ID is S67890
In this example, the Student
class overrides the greet
method it inherited from the Person
class. It still calls the parent class’s greet
method using super.greet()
, but it extends the functionality by including the student’s ID.
Common Pitfalls and Best Practices
While inheritance is powerful, it's important to be mindful of a few common pitfalls.
- Overusing Inheritance
- Inheritance should be used when there is a clear “is-a” relationship between the classes. For example, a
Student
is aPerson
. Avoid using inheritance simply to share code between classes that do not have a logical parent-child relationship. - Understanding Method Overriding
- When overriding methods, always consider whether the original method from the parent class should be called using
super
. This ensures that the subclass doesn’t inadvertently lose important functionality from the parent class. - Using Composition Where Appropriate
- Sometimes, composition (where one class contains an instance of another class) can be a better choice than inheritance, especially when the relationship is more of a “has-a” rather than “is-a” relationship.
Summary
- Inheritance
- Allows a class to inherit properties and methods from another class, promoting code reuse.
extends
Keyword- Used to create a subclass that inherits from a parent class.
super
Keyword- Used to call the constructor and methods of the parent class within a subclass.
- Method Overriding
- Allows a subclass to provide a specific implementation of a method that is inherited from the parent class.
Putting It Into Action
Inheritance in a School System
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>School System</title>
</head>
<body>
<h1>School System</h1>
<div id="output"></div>
<script>
// Base class Person
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, my name is ${this.name}`;
}
outputToDOM(message) {
const outputDiv = document.getElementById('output');
const p = document.createElement('p');
p.textContent = message;
outputDiv.appendChild(p);
}
}
// Student class extending Person
class Student extends Person {
constructor(name, studentID) {
super(name); // Calls the constructor of Person
this.studentID = studentID;
}
getDetails() {
return `${this.name} (ID: ${this.studentID})`;
}
}
const student = new Student('John Doe', 'S12345');
// console output
console.log(student.greet()); // Outputs: Hello, my name is John Doe
console.log(student.getDetails()); // Outputs: John Doe (ID: S12345)
// DOM output
student.outputToDOM(student.greet());
student.outputToDOM(student.getDetails());
</script>
</body>
</html>
In this example, the Student
class inherits from the Person
class. The greet
method is inherited from Person
, while the Student
class adds a new method getDetails
to provide more specific information about the student.
Challenge
Now that you've seen how inheritance works in a simple school system, extend this example with the following challenge.
- Create a new class called
Teacher
that extends thePerson
class. Add asubject
property and a method to return the teacher's details. - Override the
greet
method in theTeacher
class to include the subject they teach.
In order to check your learning, you should attempt to create a solution before revealing the provided solution below.
// JavaScript Code for the Challenge Solution
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, my name is ${this.name}`;
}
}
class Teacher extends Person {
constructor(name, subject) {
super(name); // Calls the constructor of Person
this.subject = subject;
}
greet() {
return `${super.greet()} and I teach ${this.subject}`;
}
getDetails() {
return `${this.name} teaches ${this.subject}`;
}
}
const teacher = new Teacher('Mr. Smith', 'Mathematics');
console.log(teacher.greet()); // Outputs: Hello, my name is Mr. Smith and I teach Mathematics
console.log(teacher.getDetails()); // Outputs: Mr. Smith teaches Mathematics