Skip to content

Class Inheritance

In the previous chapters, we explored JavaScript's object model, which is prototype-based. While it offers simplicity, understanding it can be more challenging than traditional class-instance models, and implementing inheritance often requires extensive code to correctly manage the prototype chain.

Is there a simpler way? Yes! The class keyword was introduced in ES6 to simplify class definitions in JavaScript.

Let's revisit how we implemented Student using functions:

javascript
function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

Using the new class keyword, we can define Student like this:

javascript
class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

Comparing the two, the class definition combines the constructor and methods into a single block, avoiding the need for separate prototype assignments like Student.prototype.hello = function () {...}.

Creating a Student object remains unchanged:

javascript
let xiaoming = new Student('Ming');
xiaoming.hello();

Class Inheritance

Another significant advantage of using class is easier inheritance. For example, when deriving PrimaryStudent from Student, we no longer need to consider intermediary objects or prototype constructors. We simply use extends:

javascript
class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // Remember to call the parent's constructor!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

Here, PrimaryStudent is also defined using the class keyword, and extends indicates that it inherits from Student. The subclass constructor may differ; for instance, PrimaryStudent requires both name and grade, and it calls the parent constructor with super(name) to initialize the name property.

PrimaryStudent automatically inherits the hello method from Student, while we define an additional myGrade method in the subclass.

Difference Between ES6 Classes and Prototype Inheritance

ES6's class syntax does not change the underlying prototype-based inheritance; it merely simplifies the implementation of the prototype chain that we previously had to write manually. In essence, class greatly reduces the boilerplate code associated with prototype chains.

Exercise

Now, use the class syntax to redefine Cat, making it inherit from the existing Animal class, and add a method say() that returns the string 'Hello, xxx!':

javascript
class Animal {
    constructor(name) {
        this.name = name;
    }
}

// FIXME:
class Cat extends Animal {
    say() {
        return 'Hello, ' + this.name + '!';
    }
}

// Test:
let kitty = new Cat('Kitty');
let doraemon = new Cat('Doraemon');
if ((new Cat('x') instanceof Animal)
    && kitty 
    && kitty.name === 'Kitty'
    && kitty.say
    && typeof kitty.say === 'function'
    && kitty.say() === 'Hello, Kitty!'
    && kitty.say === doraemon.say)
{
    console.log('test passed!');
} else {
    console.log('test failed!');
}

This exercise requires browser support for ES6 classes. If you encounter a SyntaxError, please try using a more recent browser.

Class Inheritance has loaded