Using Inheritance So You Don't Repeat Yourself & Notes.
Tags: javascript, devops, tutorial, beginners
Published: 2021-06-10 21:34:42

* There's a principle in programming called Don't Repeat Yourself (DRY). The reason repeated code is a problem is because any change requires fixing code in multiple places. This usually means more work for programmers and more room for errors.

* Notice in the example below that the `eat` method is shared by `Cat` and `Bear`:
```
function Cat(name) {
  this.name = name;
}

Cat.prototype = {
  constructor: Cat,
  eat: function() {
    console.log("nom nom nom");
  }
};

function Bear(name) {
  this.name = name;
}

Bear.prototype = {
  constructor: Bear,
  eat: function() {
    console.log("nom nom nom");
  }
};
```
* The `eat` method is repeated in two places. The code can be edited to follow the DRY principle by creating a `supertype` (or parent) called `Animal`:
```
function Animal() { }

Animal.prototype = {
  constructor: Animal,
  eat: function() {
    console.log("nom nom nom");
  }

};
```
* Since `Animal` includes the `eat` method, you can remove it from `Cat` and `Bear`:
```
function Cat(name) {
  this.name = name;
}

Cat.prototype = {
  constructor: Cat
};

function Bear(name) {
  this.name = name;
}

Bear.prototype = {
  constructor: Bear

};

function Animal() { }

Animal.prototype = {
  constructor: Animal,
  eat: function() {
    console.log("nom nom nom");
  }

};
```
* We just edited the code in the spirit of DRY by moving the `eat` method to the `Animal` `supertype`.


#### Inherit Behaviors from a Supertype
* In the previous post, we created a `supertype` called `Animal` that defined behaviors shared by all animals:
```
function Animal() { }
Animal.prototype.eat = function() {
  console.log("nom nom nom");
};
```
* This and the next part will cover how to reuse `Animal's` methods inside `Dog` without defining them again. It uses a technique called inheritance. This challenge covers the first step: make an instance of the `supertype` (or parent). 
```
let animal = Object.create(Animal.prototype);
```
* You could also use this, 
```
let animal = new Animal();
```
* but there are some disadvantages when using this syntax for inheritance, which are too complex for the scope of this example.

* `Object.create(obj)` creates a new object, and sets `obj` as the new object's `prototype`. Recall that the `prototype` is like the "recipe" for creating an object. By setting the `prototype` of `animal` to be `Animal's` `prototype`, you are effectively giving the `animal` instance the same "recipe" as any other instance of `Animal`.
```
function Animal() { }

Animal.prototype = {
  constructor: Animal,
  eat: function() {
    console.log("nom nom nom");
  }
};

let beagle = Object.create(Animal.prototype)

beagle.eat(); // will display nom nom nom 
console.log(beagle instanceof Animal); // will display true
```


#### Setting the Child's Prototype to an Instance of the Parent
* In the previous section you saw the first step for inheriting behavior from the `supertype` (or parent) `Animal`: making a new instance of `Animal`.

* This post covers the next step: set the `prototype` of the `subtype` (or child)—in this case, `Dog`—to be an instance of `Animal`.
```
Dog.prototype = Object.create(Animal.prototype);
```
* Remember that the `prototype` is like the "recipe" for creating an object. In a way, the recipe for `Dog` now includes all the key "ingredients" from `Animal`.
```
function Animal() { }

Animal.prototype = {
  constructor: Animal,
  eat: function() {
    console.log("nom nom nom");
  }
};

function Dog() { }

Dog.prototype = Object.create(Animal.prototype); // <----

let beagle = new Dog();
beagle.eat(); // will display when console.log nom nom nom
```
* `beagle` inherits all of `Animal's` properties, including the `eat` method.


#### Resetting an Inherited Constructor Property
* When an object inherits its `prototype` from another object, it also inherits the supertype's constructor property.

* Here's an example:
```
function Dog() { }
Dog.prototype = Object.create(Animal.prototype);
let beagle = new Dog();
beagle.constructor
```
* But `beagle` and all instances of `Dog` should show that they were constructed by `Dog` and not `Animal`. To do so, you can manually set `Dog's` constructor property to the Dog object:
```
function Animal() { }
function Dog() { }

Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.constructor = Dog;

let beagle = new Dog();

console.log(beagle.constructor); // will display [Function: Dog]
```

#### Add Methods After Inheritance
* A constructor function that inherits its `prototype` object from a `supertype` constructor function can still have its own methods in addition to inherited methods.
* For example, `Dog` is a constructor that inherits its `prototype` from Animal:
```
function Animal() { }
Animal.prototype.eat = function() { console.log("nom nom nom"); };

function Dog() { }
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
```
* In addition to what is inherited from `Animal`, you want to add behavior that is unique to `Dog` objects. Here, `Dog` will get a `bark()` function. Functions are added to `Dog's` `prototype` the same way as any constructor function:
```
Dog.prototype.bark = function() {
  console.log("Woof!")
};
```
* Now instances of `Dog` will have both `eat()` and `bark()` methods.
```
function Animal() { }
Animal.prototype.eat = function() { console.log("nom nom nom"); };

function Dog() { }

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {  // <----
  console.log("Woof!")
}

let beagle = new Dog();
beagle.eat();
beagle.bark()
```
* `beagle.eat()` would display the string `nom nom nom` in the console, and `beagle.bark()` would display the string `Woof!`.