The Web Developer Bootcamp 2024

Colt Steele

Back to OOP Index Page


oop - Extends & Super Keywords

We learned that when we build a class, we use a constructor to define the base variables, that can be accessed, with all of the methods of that class. Let's use the standard "dog", "cat" classes for demonstration:

  • Javascript
  • // Dog class
  • class Dog {
    • constructor ( name, age ) {
      • this.name = name;
      • this.age = age;
    • }
    • // create the age method
    • age() {
      • return `${this.name} is ${this.age} years old.`;
    • }
    • // create the eat method
    • eat() {
      • return `${this.name} is eating.`;
    • }
    • // create the meow method
    • bark(){
      • return `${this.name} goes Woof, Woof, Bark, Bark.`;
    • }
  • }
and a Cat class:
  • Javascript
  • // Cat class
  • class Cat {
    • constructor ( name, age ) {
      • this.name = name;
      • this.age = age;
    • }
    • // create the age method
    • age() {
      • return `${this.name} is ${this.age} years old.`;
    • }
    • // create the eat method
    • eat() {
      • return `${this.name} is eating.`;
    • }
    • // create the bark method
    • meow(){
      • return `${this.name} goes Meeooww, purr, purr.';
    • }
  • }

Now we can call:

  • const Chico = Dog( 'Chico' );
we now have a Chico object of the Dog class, and we now have access to the methods contained within the Dog class. So if we call:
  • Chico.eat()
we get: "Chico is eating", and if we call:
  • Chico.bark()
we get: "Chico goes Woof, Woof, Bark, Bark".

And of course the Cat class would work exactly the same, except there is no meow method for the Dog class, and there is no bark method for the Cat class.

What's important to note here is that we've created two different classes, each with their own, individual constructor. We also notice that there is some duplication here between the two classes. Both classes share the same variables in their respective constructors, and the also share the eat() method.

Wouldn't it be nice if we could simplify the coding, and create a common constructor that would have all of the common elements included, that we could access from all of our individual classes? This is where Extends comes in.

Extends

To accomplish a "common" constructor, we can create a "parent" constructor class, that contains all of the common elements, like so:

  • Javascript
  • // Parent constructor class
  • class Pets {
    • constructor ( name, age ) {
      • this.name = name;
      • this.age = age;
    • }
    • // create the age method
    • age() {
      • return `${this.name} is ${this.age} years old.`;
    • }
    • // create the eat method
    • eat() {
      • return `${this.name} is eating.`;
    • }
  • }

We can now remove all of the common elements from our original classes, so that all that is left are the elements that are unique to each class, like so:

  • Javascript
  • // Dog class
  • class Dog {
    • // create the bark method
    • bark() {
      • return `${this.name} goes Woof, Woof, Bark, Bark.`;
    • }
  • }
and a Cat class:
  • Javascript
  • // Cat class
  • class Cat {
    • // create the meow method
    • meow() {
      • return `${this.name} goes Meeooww, purr, purr.';
    • }
  • }

We now have a fully functional Pets Parent constructor class that supports both the Dog and Cat classes. Note that at this point, we will no longer be able to call the Dog or Cat class, and maintain the full original functionality. If we try we just get empty objects.

So we need a way to connect the Dog and Cat classes to our Pets Parent constructor. To do this we use the extends keyword with our Dog and Cat classes, like so:

  • class Dog extends Pets {
  • and
  • class Cat extends Pets {

This now extends our Dog and Cat classes to make the functionality of the Pets class available. We can now create our Dog or Cat objects, just like in the original separate classes, and retain all of the original functionality.

  • const Chico = Dog( 'Chico' );
  • const Jitters = Cat( 'Jitters' );
and we now have a Chico object of the Dog class, and a Jitters object of the Cat class. And all of the methods contained within the Pets Parent constructor are available for each class to use, plus each individual Dog and Cat class methods are available for each individual class respectively.

So, Chico.eat() will return "Chico is eating", and Jitters.eat() will return "Jitters is eating". Likewise, Chico.bark() will return "Chico goes Woof, Woof, Bark, Bark", and Jitters.meow() returns "Jitters goes Meeooww, purr, purr".


One last thing to note here about extends. We have an eat() method in our Pets Parent constructor class, but what if we created a new eat() method in our Dog class, like so:

  • // create the NEW eat() method inside the Dog class
  • eat() {
    • return `${this.name} scarf's his food like a dummy.`;
  • }
Remember, our Dog class does not contain an eat() method. So if we add the new eat() method into our Dog class, and our other eat() method still resides in our Pets Parent constructor class, and we then call Chico.eat(), which eat() method would it use?

Well, it returns "Chico scarf's his food like a dummy", which means that you can override an existing method, that is contained inside the Parent constructor class, by creating a new method within the individual class, (in this case, the Dog class).

To be more specific, when you create a class object, like our Dog object, and then call a method of the object, Javascript will first look inside the local class that the object represents, and if it doesn't find the method there, it will then look to the Parent constructor class for the method.

Super

Now we showed how we could use extends to create a "parent" constructor class so that we could share common properties and methods. But what if we needed some extra properties in one of our classes, that we didn't need in the other classes? This is where the keyword super comes in.

Let's take our Cat class from above and add a new property called livesLeft. Let's see the code for the updated Cat class:

  • Javascript
  • // Cat class
  • class Cat {
    • constructor ( name, age, livesLeft = 9 ) {
      • super ( name, age );
      • this.livesLeft = livesLeft;
  • }
  • // create the meow method
  • meow() {
    • return `${this.name} goes Meeooww, purr, purr.';
  • }
Because we are adding new properties to our existing "parent" constructor class, we now need to bring back our constructor in our Cat class. We then use super to tell our Cat class which properties to "pull" in from the "parent" constructor. In this case, the name property. We then need to assign the new livesLeft property to the Cat class with the this keyword which makes it available for use in any and all methods that we create in the Cat class.

And now we can add a new method lives:

  • // add lives method
  • lives() {
    • return `${this.name} has ${this.livesLeft} lives left.
  • }

and when we create the "Jitters" object:

  • const jitters = Cat('Jitters');
and we call:
  • jitters.livesLeft();
we now get "Jitters has 9 lives left".

Remember, we can not call livesLeft() from a Dog object because livesLeft() is unique to the Cat class, just like the meow() method.
Final Code

And here is our Final Code:

  • Javascript
  • // create Parent constructor class
  • class Pets {
    • constructor ( name, age ) {
      • this.name = name;
      • this.age = age;
    • }
    • // create the age method
    • age() {
      • return `${this.name} is ${this.age} years old.`;
    • }
    • // create the eat method
    • eat() {
      • return `${this.name} is eating.`;
    • }
  • }
  •  
  • // create Dog class
  • class Dog extends Pets {
    • // create the bark method
    • bark() {
      • return `${this.name} goes Woof, Woof, Bark, Bark.`;
    • }
    • // create the modified eat method
    • eat() {
      • return `${this.name} scarf's his food.`;
    • }
  • }
  •  
  • // create Cat class
  • class Cat extends Pets {
    • constructor ( name, age, livesLeft = 9 ) {
      • super(name, age);
      • this.livesLeft = livesLeft;
    • }
    • // create the meow method
    • meow() {
      • return `${this.name} goes Meeooww, purr, purr.';
    • }
    • // create the livesLeft method
    • lives() {
      • return `${this.name} has ${livesLeft} lives left.';
    • }
  • }

Final Code Explained:

  • 1. We created a parent constructor class (Pets), that contains all of the shared properties and methods for all of our classes.
    • - (the name, and age properties, and the default eat() method)
  • 2. We created the Dog class that:
    • - has it's unique bark() method
    • - has overridden the default eat() method
  • 3. We created the Cat class that:
    • - has it's unique meow() method
    • - has added a new unique livesLeft() method

and now we can create our Dog and Cat objects:

  • // create the class objects
  • const myDog = new Dog('Chico');
  • const myCat = new Cat('Jitters');
and finally, call the Dog and Cat methods:
  • // call the Dog methods
  • console.log(myDog.age());
  • console.log(myDog.eat());
  • console.log(myDog.bark());
  •  
  • // call the Cat methods
  • console.log(myCat.age());
  • console.log(myCat.eat());
  • console.log(myCat.meow());


Back to Top