Skip to content

mikeyhodl/Inheritance-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Inheritance-js

INHERITANCE I

Imagine our doggy daycare is so successful that we decide to expand the business and open a kitty daycare. Before the daycare opens, we need to create a Cat class so we can quickly generate Cat instances. We know that the properties in our Cat class (name, behavior) are similar to the properties in our Dog class, though, there will be some differences, because of course, cats are not dogs.

Let’s say that our Cat class looks like this:

class Cat { constructor(name, usesLitter) { this._name = name; this._usesLitter = usesLitter; this._behavior = 0; }

get name() { return this._name; }

get usesLitter() { return this._usesLitter; }

get behavior() { return this._behavior; }

incrementBehavior() { this._behavior++; } } In the example above, we create a Cat class. It shares a couple of properties (_name and _behavior) and a method (.incrementBehavior()) with the Dog class from earlier exercises. The Cat class also contains one additional property (_usesLitter), that holds a boolean value to indicate whether a cat can use their litter box.

When multiple classes share properties or methods, they become candidates for inheritance — a tool developers use to decrease the amount of code they need to write.

With inheritance, you can create a parent class (also known as a superclass) with properties and methods that multiple child classes (also known as subclasses) share. The child classes inherit the properties and methods from their parent class.

Let’s abstract the shared properties and methods from our Cat and Dog classes into a parent class called Animal.

class Animal { constructor(name) { this._name = name; this._behavior = 0; }

get name() { return this._name; }

get behavior() { return this._behavior; }

incrementBehavior() { this._behavior++; } } In the example above, the Animal class contains the properties and methods that the Cat and Dog classes share (name, behavior, .incrementBehavior()).

The diagram to the right shows the relationships we want to create between the Animal, Cat, and Dog classes.

INHERITANCE II

In the last exercise, we created a parent class named Animal for two child classes named Cat and Dog.

The Animal class below contains the shared properties and methods of Cat and Dog.

class Animal { constructor(name) { this._name = name; this._behavior = 0; }

get name() { return this._name; }

get behavior() { return this._behavior; }

incrementBehavior() { this._behavior++; } } The code below shows the Cat class that will inherit information from the Animal class.

class Cat { constructor(name, usesLitter) { this._name = name; this._usesLitter = usesLitter; this._behavior = 0; }

get name() { return this._name; }

get behavior() { return this._behavior; }

get usesLitter() { return this._usesLitter; }

incrementBehavior() { this._behavior++; } } To the right, in main.js, you will put what you learned to practice by creating a parent class named HospitalEmployee.

Instructions 1. In the next few exercises, you will create two subclasses (Doctor and Nurse) from a parent class named HospitalEmployee. Below, we have listed the properties and methods you will find in the Doctor and Nurse classes.

Doctor Properties: _name, _remainingVacationDays (set to 20 inside the constructor()), _insurance Methods: .takeVacationDays() Nurse Properties: _name, _remainingVacationDays (set to 20 inside constructor()), _certifications Methods: .takeVacationDays(), .addCertification() In main.js, create a parent class named HospitalEmployee. Add a constructor with name as an argument.

INHERITANCE III

Inheritance III We’ve abstracted the shared properties and methods of our Cat and Dog classes into a parent class called Animal (See below).

class Animal { constructor(name) { this._name = name; this._behavior = 0; }

get name() { return this._name; }

get behavior() { return this._behavior; }

incrementBehavior() { this._behavior++; } } Now that we have these shared properties and methods in the parent Animal class, we can extend them to the subclass, Cat.

class Cat extends Animal { constructor(name, usesLitter) { super(name); this._usesLitter = usesLitter; } } In the example above, we create a new class named Cat that extends the Animal class. Let’s pay special attention to our new keywords: extends and super.

The extends keyword makes the methods of the animal class available inside the cat class. The constructor, called when you create a new Cat object, accepts two arguments, name and usesLitter. The super keyword calls the constructor of the parent class. In this case, super(name) passes the name argument of the Cat class to the constructor of the Animal class. When the Animal constructor runs, it sets this._name = name; for new Cat instances. _usesLitter is a new property that is unique to the Cat class, so we set it in the Cat constructor. Notice, we call super on the first line of our constructor(), then set the usesLitter property on the second line. In a constructor(), you must always call the super method before you can use the this keyword — if you do not, JavaScript will throw a reference error. To avoid reference errors, it is best practice to call super on the first line of subclass constructors.

Below, we create a new Cat instance and call its name with the same syntax as we did with the Dog class:

const bryceCat = new Cat('Bryce', false); console.log(bryceCat._name); // output: Bryce In the example above, we create a new instance the Cat class, named bryceCat. We pass it 'Bryce' and false for our name and usesLitter arguments. When we call console.log(bryceCat._name) our program prints, Bryce.

In the example above, we abandoned best practices by calling our _name property directly. In the next exercise, we’ll address this by calling an inherited getter method for our name property.

Instructions 1. In this exercise, you will begin to create the Nurse class as a child of the HospitalEmployee class. Remember the Nurse class has the following properties and methods:

Nurse Properties: _name, _remainingVacationDays (set to 20 inside constructor()), _certifications Methods: .takeVacationDays(), .addCertification() Under HospitalEmployee, create an empty class named Nurse that extends HospitalEmployee.

INHERITANCE IV

Now that we know how to create an object that inherits properties from a parent class let’s turn our attention to methods.

When we call extends in a class declaration, all of the parent methods are available to the child class.

Below, we extend our Animal class to a Cat subclass.

class Animal { constructor(name) { this._name = name; this._behavior = 0; }

get name() { return this._name; }

get behavior() { return this._behavior; }

incrementBehavior() { this._behavior++; } }

class Cat extends Animal { constructor(name, usesLitter) { super(name); this._usesLitter = usesLitter; } }

const bryceCat = new Cat('Bryce', false); In the example above, our Cat class extends Animal. As a result, the Cat class has access to the Animal getters and the .incrementBehavior() method.

Also in the code above, we create a Cat instance named bryceCat. Because bryceCat has access to the name getter, the code below logs 'Bryce' to the console.

console.log(bryceCat.name); Since the extends keyword brings all of the parent’s getters and methods into the child class, bryceCat.name accesses the name getter and returns the value saved to the name property.

Now consider a more involved example and try to answer the following question: What will the code below log to the console?

bryceCat.incrementBehavior(); // Call .incrementBehavior() on Cat instance console.log(bryceCat.behavior); // Log value saved to behavior The correct answer is 1. But why?

The Cat class inherits the _behavior property, behavior getter, and the .incrementBehavior() method from the Animal class. When we created the bryceCat instance, the Animal constructor set the _behavior property to zero. The first line of code calls the inherited .incrementBehavior() method, which increases the bryceCat _behavior value from zero to one. The second line of code calls the behavior getter and logs the value saved to _behavior (1).

INHERITANCE V

Inheritance V In addition to the inherited features, child classes can contain their own properties, getters, setters, and methods.

Below, we will add a usesLitter getter. The syntax for creating getters, setters, and methods is the same as it is in any other class.

class Cat extends Animal { constructor(name, usesLitter) { super(name); this._usesLitter = usesLitter; }

get usesLitter() { return this._usesLitter; } } In the example above, we create a usesLitter getter in the Cat class that returns the value saved to _usesLitter.

Compare the Cat class above to the one we created without inheritance:

class Cat { constructor(name, usesLitter) { this._name = name; this._usesLitter = usesLitter; this._behavior = 0; }

get name() { return this._name; }

get usesLitter() { return this._usesLitter; }

get behavior() { return this._behavior; }

incrementBehavior() { this._behavior++; } } We decreased the number of lines required to create the Cat class by about half. Yes, it did require an extra class (Animal), making the reduction in the size of our Cat class seem moot. However, the benefits (time saved, readability, efficiency) of inheritance grow as the number and size of your subclasses increase.

One benefit is that when you need to change a method or property that multiple classes share, you can change the parent class, instead of each subclass.

Before we move past inheritance, take a moment to see how we would create an additional subclass, called Dog.

class Dog extends Animal { constructor(name) { super(name); } } This Dog class has access to the same properties, getters, setters, and methods as the Dog class we made without inheritance, and is a quarter the size.

Now that we’ve abstracted animal daycare features, it’s easy to see how you can extend Animal to support other classes, like Rabbit, Bird or even Snake.

Static Methods

Sometimes you will want a class to have methods that aren’t available in individual instances, but that you can call directly from the class.

Take the Date class, for example — you can both create Date instances to represent whatever date you want, and call static methods, like Date.now() which returns the current date, directly from the class. The .now() method is static, so you can call it directly from the class, but not from an instance of the class.

Let’s see how to use the static keyword to create a static method called generateName method in our Animal class:

class Animal { constructor(name) { this._name = name; this._behavior = 0; }

static generateName() { const names = ['Angel', 'Spike', 'Buffy', 'Willow', 'Tara']; const randomNumber = Math.floor(Math.random()*5); return names[randomNumber]; } } In the example above, we create a static method called .generateName() that returns a random name when it’s called. Because of the static keyword, we can only access .generateName() by appending it to the Animal class.

We call the .generateName() method with the following syntax:

console.log(Animal.generateName()); // returns a name You cannot access the .generateName() method from instances of the Animal class or instances of its subclasses (See below).

const tyson = new Animal('Tyson'); tyson.generateName(); // TypeError The example above will result in an error, because you cannot call static methods (.generateName()) on an instance (tyson).

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published