Skip to content
By Sebastian Porto

ES6 classes and JavaScript prototypes

ES6 introduces the class keyword. At first glance this looks like a completely new object model that follows classical languages like Java or Ruby, but behind the new syntax there is nothing new. The class syntax just provides an alternative way to create plain old JavaScript objects.

In this post I want to explain ES6 classes in the context of the JavaScript objects they produce.

Proto refresher

Let's start with a little refresher of prototype chains in JavaScript.

Every object in JavaScript has a special related object called the prototype. Prototypes are simple ways to share behaviour and data between multiple objects.

In code a prototype looks like this:

vehicle.__proto__ = machine
// machine is the prototype of vehicle

car.__proto__ = vehicle
// vehicle is the prototype of car

This is a prototype chain:

car -> vehicle -> machine

When JavaScript looks for a property that doesn't exist in a particular object (e.g. car), it will attempt to look for that property in each object on the prototype chain (e.g. first in vehicle, then in machine). It will walk along the chain until it finds the attribute or return undefined if it can't be found.

If you'd like to get a better understanding of this, please see this post.

ES6

In JavaScript we have Function Constructors, which have been the common way to build new objects until ES6. Unfortunately Function Constructors can be quite confusing to understand (especially if you want to model inheritance). To alleviate this, ES6 introduces the class syntax.

Classes in ES6 don't add any functionality to what we already have in the language, they are just a simpler syntax for building the same objects as we had before.

ES6 Classes

Take an ES6 class like this:

class Car {
    constructor(name) {
        this.kind = 'Car';
        this.name = name;
    }

    printName() {
        console.log('this.name');
    }
}

Let's create a car:

var mazda = new Car('Mazda');

This creates a JavaScript object. This object gets a prototype automatically assigned to it, more on this in a bit.

First let's explore the attributes assigned in the constructor.

mazda.hasOwnProperty('kind') // true
mazda.hasOwnProperty('name') // true

kind and name are properties directly assigned on the new mazda object. If you were to represent this object in an object literal it would look something like:

var mazda = {
    kind: 'Car',
    name: 'Mazda'
}

What about printName, where is this?

mazda.hasOwnProperty('printName') // false

It is not in the object itself, but we can call it:

mazda.printName() //Mazda

So we can deduce that it is somewhere in the prototype chain of mazda. Let's try the prototype of mazda:

mazda.__proto__.hasOwnProperty('printName') // true

This method was not copied when creating mazda, it is in the prototype.

Where did this prototype object came from?

This object is created automatically by JavaScript when declaring a class and assigned to all the instances of the class.

Let's create two cars now:

var mazda = new Car('Mazda');
var bmw   = new Car('BMW');

If we compare the prototypes of mazda and bmw we find that they are the same object:

mazda.__proto__ == bmw.__proto__ //-> true

Thus the method printName is being shared by all instances of Car. Adding methods on the prototypes of objects in JavaScript is an efficient way to conserve memory (as opposed to copying the methods on each object).

Prototypes are just objects that can be changed at runtime

Let's change the method printName after creating an instance:

var mazda = new Car('Mazda');
var bmw   = new Car('BMW');

mazda.__proto__.printName = function () { return 'Oops' }
bmw.printName() //-> Oops

Changing the method on the prototype of mazda changes the result on bmw as well. This is because they share the same prototype and we are changing that object.

What if we create yet another Car after doing this?

var honda = new Car('Honda');
honda.printName() // Oops

Oops, there is no going back, we have already modified the prototype for all instances of Car, now and in the future.

Remember, classes in JavaScript are not a blueprint like in other languages. They just define objects that can be modified at will in runtime.

To recap:

class Car {
    constructor(name) {
        // - this is the newly created object
        // - anything assigned here will be assigned
        //   to the object directly
        this.kind = 'Car';
        this.name = name;
    }

    // - class methods are assigned to the prototype of 
    //   the newly created object
    // - this prototype object will be shared by all instances
    printName() { 
        console.log('this.name');
    }
}

Inheritance

ES6 classes make inheritance much easier to model and understand than before:

class Vehicle {
    constructor(name) {
        this.kind = 'Vehicle';
        this.name = name;
    }

    printName() {
        console.log(this.name);
    }
}

class Car extends Vehicle {
    constructor(name) {
        super(name); //call the parent method with super
        this.kind = 'Car';
    }

}

Let's make some vehicles:

var boat  = new Vehicle('Boat');
var mazda = new Car('Mazda');

What is the relationship between these two?

boat.__proto__ == mazda.__proto__ // false

Ok, boat and mazda don't share a prototype. But we can call printName on both:

boat.printName() //-> Boat
mazda.printName() //-> Mazda

So, there's something shared here. What is happening is that the keyword extends is instructing JavaScript to create a prototype chain for us:

mazda.__proto__.__proto__ == boat.__proto__ //-> true

Let's double check by modifying this prototype:

mazda.__proto__.__proto__.printName = function () { console.log('Ha') }
boat.printName() // Ha

Conclusion

ES6 classes just create the same prototype chains we know and love from previous versions of JavaScript, but with a much saner syntax than Function Constructors.

I hope this post helps to clarify how classes work in ES6 and how they relate to objects in JavaScript.

Latest Articles by Our Team

Our expert team of designers and developers love what the do and enjoy sharing their knowledge with the world.

We Hire Only the Best

reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.

We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.

Free Community Workshops

We created the Ruby on Rails InstallFest and Ruby on Rails Development Hub to help introduce new people to software development and to help existing developers hone their skills. These workshops provide invaluable mentorship to train developers, addressing key skills shortages in the industry. Software development is a great career choice for all ages and these events help you get started and skilled up.

  • Installfest

    Installfest

    The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.

    Learn more about Installfest

  • Development Hub

    Development Hub

    The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.

    Learn more about Development Hub

  • Webinars

    Webinars

    Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.

    Learn more about webinars

Get the “reinteractive Review” Monthly Email