Skip to main content

30. Class Inheritance


MDN Docs Basically, don't repeat yourself. Both of these contain a getFullName() method.

class Employee {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

getFullName() {
return `${this.firstName} ${this.lastName}`;
}

getInitials() {
return this.firstName[0] + this.lastName[0];
}
}

class Manager {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

getFullName() {
return `${this.firstName} ${this.lastName}`;
}

getInitials() {
return this.firstName[0] + this.lastName[0];
}

sendPerformanceReview() {
console.log(`Sent performance review for current quarter`);
}
}

This is an example where we can use inheritance to reduce the duplication. We can notice that Manager implements all the methods from Employee (and has extra ones). Thus, Manager inherits from Employee. Which means that Manager will get all the methods defined on Employee. Here's how you write it, using the extends keyword:

class Employee {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

getFullName() {
return `${this.firstName} ${this.lastName}`;
}

getInitials() {
return this.firstName[0] + this.lastName[0];
}
}

class Manager extends Employee {
sendPerformanceReview() {
console.log(`Sent performance review for current quarter`);
}
}

Overriding methods


This can be used to add functionality to the existing methods. This one adds the (manager) to the getFullName() method:


// assuming the same class definition for Employee
class Employee {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

getFullName() {
return `${this.firstName} ${this.lastName}`;
}

getInitials() {
return this.firstName[0] + this.lastName[0];
}
}

//extends the Employee class and adds a new method sendPerformanceReview() :)
class Manager extends Employee {
getFullName() {
return `${this.firstName} ${this.lastName} (manager)`;
}

sendPerformanceReview() {
console.log(`Sent performance review for current quarter`);
}
}

Inheritance reduces code duplication without losing control over customization.

Try it


Define the necessary classes so that we are able to run the 2 blocks of code below:

const user = new User("Sam", "Green", 17);
user.canVote(); // false
user.getFullName(); // "Sam Green"
const admin = new Admin("Alex", "Blue", 20);
admin.canVote(); // true
admin.getFullName(); // "Alex Blue [admin]"
admin.updateConfig(); // "Config updated"

The User class should have 2 instance methods:

  • canVote() which returns true when the age is 18 years or above, false otherwise.
  • getFullName() which returns the first name and last name separated by a space character.

The Admin class should have 3 instance methods:

  • canVote() which returns true when the age is 18 years or above, false otherwise.
  • getFullName() which returns the first name and last name separated by a space character followed by [admin] at the end.
  • updateConfig() which returns the string "Config updated".
  • Use inheritance to avoid code duplication.
// Start by outlining the classes and the methods.
class User {
canVote() {

}
getFullName(){

}
}

class Admin extends User {

}

//then finish the methods
class User {
constructor (firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
canVote() {
if (this.age >=18) {
return true;
}
return false;

}
getFullName(){
return `${this.firstName} ${this.lastName}`

}
}

class Admin extends User {
getFullName(){
return `${this.firstName} ${this.lastName} [admin]`
}
updateConfig() {
return "Config updated"
}
}

Mobile Phone


Android & iOS

Define the necessary classes so that we are able to run the 2 blocks of code below:

const galaxy = new Android("Galaxy", 400);
galaxy.getDescription(); // "The Galaxy costs 400"
galaxy.getVersion(); // 12
const iphone = new iOS("iPhone", 500);
iphone.getDescription(); // "The iPhone costs 500"
iphone.getVersion(); // 15

Each mobile phone takes a name and a price and should have 2 methods:

  • getDescription() which returns a string describing the phone.
  • getVersion() which is always 12 for android phones and always 15 for iOS phones.

Use inheritance to avoid code duplication. Also, make sure that your answer makes sense. The goal is not to write the shortest code, but, rather a code that represents the reality of Android and iOS.

// define a class that both the other classes can extend
class MobilePhone {
constructor (name, cost) {
this.name = name;
this.cost = cost;
}
getDescription(){
return `The ${this.name} costs ${this.cost}` //both phones take this identically
}
}

class Android extends MobilePhone {
getVersion(){
return 12; // this is different here
}
}

class iOS extends MobilePhone {
getVersion() {
return 15; // this is different here
}
}

// Sample usage - do not modify
const galaxy = new Android("Galaxy", 400);
console.log(galaxy.getDescription()); // "The Galaxy costs 400"
console.log(galaxy.getVersion()); // 12
const iphone = new iOS("iPhone", 500);
console.log(iphone.getDescription()); // "The iPhone costs 500"
console.log(iphone.getVersion()); // 15

Super


You use super when the class that extends the parent class adds additional parameters.

  • The super keyword is used to call functions on the parent class.
  • When overriding the constructor(), make sure to call super() first thing before accessing any other instance variable.
  • You can call functions on the parent class also using the super keyword, for example super.parentMethodName().

Try using super


Update the Admin class such that its constructor accepts a 4th parameter called userType. For example, const admin = new Admin("Alex", "Blue", 20, "superadmin"). Then, update the getFullName() to return this userType instead of the hardcoded [admin]. So, for the example above, admin.getFullName() should return Alex Blue [superadmin].

class User {
constructor(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

getFullName() {
return `${this.firstName} ${this.lastName}`;
}

canVote() {
return this.age >= 18;
}
}

class Admin extends User {
getFullName() {
return `${this.firstName} ${this.lastName} [admin]`;
}

updateConfig() {
return "Config updated";
}
}

//solution
class User {
constructor(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

getFullName() {
return `${this.firstName} ${this.lastName}`;
}

canVote() {
return this.age >= 18;
}
}

class Admin extends User {

constructor (firstName,lastName,age,userType) {
super(firstName, lastName,age, userType);
this.userType = userType;
}

getFullName() {
return `${this.firstName} ${this.lastName} [superadmin]`;
}

updateConfig() {
return "Config updated";
}
}

Gem Coin game


game-score.js

// do NOT remove the 'export default'
export default class GameScore {
// TODO: constructor, addCollectible, and getTotalScore
constructor() {
this.collectibles = [];
}
addCollectible(collectible) {
this.collectibles.push(collectible);
}
getTotalScore() {
this.sum = 0;

this.collectibles.forEach(
(collectible) => (this.sum = this.sum + collectible.getScore()),
);
return this.sum;
}
}

collectibles.js

// do NOT remove the 'export' keyword
export class Collectible {
// TODO: constructor, collect, getScore, and getCollectedMessage
constructor(name, worth) {
this.name = name;
this.worth = worth;
this.count = 0;
}

collect() {
this.count++;
}
getScore() {
return this.worth * this.count;
}

getCollectedMessage() {
return `${this.count} ${this.name}s collected`;
}
}

// do NOT remove the 'export' keyword
export class Coin extends Collectible {
// TODO: constructor and inheritance
constructor(name, worth) {
super("coin", 10);
}
}

// do NOT remove the 'export' keyword
export class Gem extends Collectible {
// TODO: constructor and inheritance
constructor(name, worth) {
super("gem", 50);
}
}

Chapter Recap


  • An important principle in programming is called DRY (Don't Repeat Yourself).
  • class Child extends Parent is the syntax for inheritance in JavaScript.
  • When a "child" class inherits from a "parent" class, the "child" class will automatically get all the methods defined on the "parent" class.
  • The super keyword is used to call functions on the parent class.
  • When overriding the constructor(), make sure to call super() first thing before accessing any other instance variable.
  • You can call functions on the parent class also using the super keyword, for example super.parentMethodName().