The idea
Many things are kinds of other things: a puppy is a dog, a car is a vehicle, a manager is an employee. Inheritance models that "is-a" relationship in code. A child class (also called a subclass) takes on the properties and methods of a parent class (the superclass) and can add its own. You write the shared code once, in the parent, instead of copying it into every class.
The extends keyword
You declare a child class with extends followed by the parent's name:
class Puppy extends Dog {
whine() {
console.log("whimper");
}
}
That is all it takes. A Puppy object can now do everything a Dog can do, plus whine().
What gets inherited
- Properties. A
Puppyhas aname, because the parent's constructor sets it. - Methods. A
Puppycan callbark()although onlyDogdefines it. - The type. A
Puppycounts as aDog, sonew Puppy("Buddy") instanceof Dogistrue. Code written for dogs accepts puppies, which later lessons use for polymorphism.
Inheritance goes one way. A plain Dog does not get whine(), and new Dog("Rex").whine() fails with TypeError because the method doesn't exist on it.
Behind the scenes, JavaScript links the child's prototype to the parent's. When you call buddy.bark(), JavaScript looks for bark on buddy, then on Puppy, then on Dog, and runs the first one it finds. This chain of lookups is called the prototype chain.
Constructors in subclasses
If the child declares no constructor, JavaScript supplies one that passes all its arguments on to the parent's constructor. That is why new Puppy("Buddy") sets the name with no extra code.
When the child needs a constructor of its own, for instance to add a new property, it must call super(...) to run the parent's constructor, and it must do so before touching this:
class Puppy extends Dog {
constructor(name, toy) {
super(name); // runs Dog's constructor
this.toy = toy;
}
}
Using this before super(), or never calling super() at all, throws ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor.
Inside a child's method, super.bark() calls the parent's version of that method. That is useful when the child wants to add to the parent's behavior without replacing it, a topic the next lesson covers.
The exercise, step by step
The starter code defines Dog with a constructor and a bark method, then creates a Puppy named "Buddy" and calls bark() and whine() on it. Puppy doesn't exist yet.
- Declare the subclass. Below
Dog, writeclass Puppy extends Dog { }. No constructor is needed, because the automatic one forwards"Buddy"toDog, andbarkis inherited. - Add the new behavior. Inside
Puppy, write awhinemethod that logs"whimper". Methods in a class body have nofunctionkeyword.
The solution in JavaScript
class Dog {
constructor(name) { this.name = name; }
bark() {
console.log("Woof! I am " + this.name);
}
}
class Puppy extends Dog {
whine() {
console.log("whimper");
}
}
let buddy = new Puppy("Buddy");
buddy.bark();
buddy.whine();
The output is:
Woof! I am Buddy
whimper
The first line comes from Dog.bark, running on a Puppy object. The second comes from the method that only Puppy has.
Common mistakes
- Forgetting
extends. A separateclass Puppy { ... }has no link toDog, sobuddy.bark()fails withTypeError: buddy.bark is not a function, and the name is never set. - Copying the parent's code. Pasting
barkintoPuppyworks, but a later fix to one copy leaves the other unchanged. Inherit instead. - Calling
supertoo late or not at all in a constructor that you wrote yourself. Makesuper(...)the first statement. - Inheriting when the relationship isn't "is-a". A
Doghas aTail, but it isn't a tail, so a property is the better tool there. Prefer a short chain of classes over a deep one. - Extending more than one class. A JavaScript class can have only one parent.
Key takeaways
class Child extends Parentgives the child all the parent's properties and methods.- A child without a constructor passes its arguments to the parent's; with one, it must call
super(...)before usingthis. - A child can add new methods, and an object of the child class is also an instance of the parent.
- Use inheritance for "is-a" relationships and keep hierarchies shallow.