📖 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
Studentis 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.
extendsKeyword- Used to create a subclass that inherits from a parent class.
superKeyword- 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
Teacherthat extends thePersonclass. Add asubjectproperty and a method to return the teacher's details. - Override the
greetmethod in theTeacherclass 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