Lesson 4
Inheritance in JavaScript Classes
Introduction

Hello again! In this part of our JavaScript Class Basics Revision, we delve into inheritance in object-oriented programming (OOP) with JavaScript. Inheritance allows us to share code across classes, thus improving readability and efficiency.

In this lesson, we'll clarify attribute and method inheritance in JavaScript using practical examples. Our lesson's blueprint includes defining inheritance, examining attribute inheritance, exploring method inheritance, and decoding the super() function in JavaScript. Ready? Let's get started!

Defining Inheritance

Inheritance involves creating a child class that inherits details from a parent class. In JavaScript, we often find scenarios where classes share common attributes or methods, which makes inheritance highly useful.

The extends keyword is used to set up inheritance, allowing one class to inherit properties and methods from another class. Here's an example featuring a parent class named Vehicle and a child class named Car:

JavaScript
1// Define the parent class 'Vehicle' 2class Vehicle { 3 // Initialize the Vehicle with color and brand attributes 4 constructor(color, brand) { 5 this.color = color; 6 this.brand = brand; 7 } 8} 9 10// Define the child class 'Car', inheriting from 'Vehicle' 11class Car extends Vehicle { 12 constructor(color, brand, doors) { 13 // Call the parent class's constructor method to set color and brand 14 super(color, brand); 15 this.doors = doors; 16 } 17}

In the Car class, the super() function inside its constructor calls the Vehicle class's constructor, enabling the inherited properties to be initialized correctly. The extends keyword signifies that Car is a subclass of Vehicle.

Inheritance types, such as Single, Multiple, Multilevel, and Hierarchical, in JavaScript cater to different needs. However, our focus in this lesson is primarily on single inheritance, where one parent class feeds one child class.

Attribute Inheritance

Attribute inheritance allows a child class to inherit the attributes of a parent class, with the exception of private fields. Private fields (declared with a # before the attribute name) are not accessible in child classes.

Consider this example featuring a parent class named Artist, and a child class named Musician:

JavaScript
1class Artist { 2 constructor(name) { 3 this.name = name; // Parent's attribute 4 } 5} 6 7class Musician extends Artist { 8 constructor(name, instrument) { 9 super(name); // Inheriting parent's attribute 10 this.instrument = instrument; // Child's own attribute 11 } 12} 13 14const john = new Musician('John Lennon', 'Guitar'); // Creating a Musician instance 15console.log(john.name); // Output: John Lennon 16console.log(john.instrument); // Output: Guitar

However, if the name attribute in the Artist class were private, it wouldn't be accessible in the Musician class:

JavaScript
1class Artist { 2 #name; // Private attribute 3 4 constructor(name) { 5 this.#name = name; // Initialize private attribute 6 } 7 8 getName() { 9 return this.#name; // Getter method for the private attribute 10 } 11} 12 13class Musician extends Artist { 14 constructor(name, instrument) { 15 super(name); 16 this.instrument = instrument; 17 } 18} 19 20const john = new Musician('John Lennon', 'Guitar'); 21console.log(john.getName()); // Output: John Lennon 22console.log(john.instrument); // Output: Guitar

The Musician class inherits the name attribute from the Artist class and also has its own unique attribute, instrument, but it accesses the name attribute via a getter method since name is private.

Method Inheritance

Similar to attributes, method or function inheritance allows a child class to inherit the methods of a parent class.

In the example below, the Car class can invoke the start method from the Vehicle class:

JavaScript
1class Vehicle { 2 constructor(brand) { 3 this.brand = brand; 4 } 5 6 start() { 7 console.log(`The ${this.brand} is starting.`); 8 } 9} 10 11class Car extends Vehicle { 12 // No new methods or attributes are added here 13} 14 15const myCar = new Car('BMW'); 16myCar.start(); // Output: The BMW is starting.

In the absence of an explicitly defined constructor in the Car class, JavaScript automatically provides a default constructor, which internally calls super() to ensure the parent class's constructor is invoked. This initialization allows the Car class to inherit the brand attribute from the Vehicle class seamlessly.

Understanding the `super()` Function

The super() function is integral in inheritance for calling parent class methods from a child class, particularly useful in method overriding and initialization. It allows a child class to extend or utilize the functionality of a parent class without directly modifying it.

For instance, when overriding a method to add or alter its behavior, super() enables calling the original method from the parent class to integrate its functionality with new enhancements:

JavaScript
1class Vehicle { 2 start() { 3 return "Vehicle is starting..."; 4 } 5} 6 7class Car extends Vehicle { 8 start() { 9 return super.start() + " Beep! Beep!"; 10 } 11} 12 13const myCar = new Car(); 14console.log(myCar.start()); // Output: Vehicle is starting... Beep! Beep!

Similarly, during initialization, super() calls the constructor method of the parent class, making sure that the child class is currently initialized, allowing the child class to add its specific attributes seamlessly:

JavaScript
1class ParentClass { 2 constructor(value) { 3 this.value = value; 4 } 5} 6 7class ChildClass extends ParentClass { 8 constructor(value, additionalValue) { 9 super(value); // Invoke parent class's constructor 10 this.additionalValue = additionalValue; 11 } 12} 13 14const childClass = new ChildClass("value", "additional_value"); 15console.log(childClass.value); // Output: value 16console.log(childClass.additionalValue); // Output: additional_value

In these ways, super() facilitates a coherent and modular approach to inheritance by allowing child classes to build upon or adapt the functionality of their parent classes efficiently and cleanly.

Lesson Summary

We've successfully explored attribute and method inheritance in JavaScript and practiced using several examples. Mastering these concepts in real-life programming can enhance both efficiency and readability. Remember, practice is essential for proficiency!

On that note, are you ready for some practice exercises? They will solidify your understanding and prepare you for more complex programming tasks. Programming is all about experimenting, learning, and problem-solving. Enjoy the journey!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.