Understanding ECMAScript 6: Class and Inheritance

Originally published at: http://www.sitepoint.com/understanding-ecmascript-6-class-inheritance/

This article is part of a web dev tech series from Microsoft. Thank you for supporting the partners who make SitePoint possible.

I’d like to share with you a series of articles about ECMAScript 6 , sharing my passion for it and explaining how it can work for you. I hope you enjoy reading them as much as I did writing them.

First, I work in Microsoft on the browser rendering engine for Project Spartan, which is a vast improvement over the Internet Explorer engine we got to know (and love?) over the years. My personal favorite feature of it is that it supports a lot of ECMAScript 6. To me, this is a massive benefit to writing large applications for the web.

We now have almost 70% of ECMAScript 6 features in Project Spartan so far according to http://kangax.github.io/compat-table/es6/and ES6 on status.modern.IE.

ES6 Compatibility Table

I love JavaScript, but when it comes to working on large projects like Babylon.js, I prefer TypeScript which is now powering Angular 2 btw. The reason is that JavaScript (or otherwise known as ECMAScript 5) doesn’t have all the syntax features I am used to from other languages I write large projects in. I miss classes and inheritance, for instance.

So without further ado, let’s get into just that:

Creating a class

JavaScript is a prototype oriented language and it is possible to simulate classes and inheritance with ECMAScript 5.

The flexibility of functions in JavaScript allows us to simulate encapsulation we are used to when dealing with classes. The trick we can use for that is to extend the prototype of an object:

var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    // Methods
    Animal.prototype.doSomething = function () {
        console.log("I'm a " + this.name);
    };
    return Animal;
})();


var lion = new Animal("Lion");
lion.doSomething();

We can see here that we defined a class with properties and methods.

The constructor is defined by the function itself (function Animal) where we can instantiate properties. By using the prototype we can define functions that will be considered like instance methods.

This works, but it assumes you know about prototypical inheritance and for someone coming from a class-based language it looks very confusing. Weirdly enough, JavaScript has a class keyword, but it doesn’t do anything. ECMAScript 6 now makes this work and allows for shorter code:

class AnimalES6 {
    constructor(name) {
        this.name = name;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();

The result is the same, but this is easier to write and read for developers who are used to writing classes. There is no need for the prototype and you can use the new keyword to define the constructor.

Furthermore, classes introduce a number of new semantics that aren’t present in the ECMAScript 5 equivalent. For example, you cannot call a constructor without new or you cannot attempt to construct methods with new. Another change is that methods are non-enumerable.

Interesting point here: Both versions can live side by side.

At the end of the day, even with the new keywords you end up with a function with a prototype where a function was added. A method here is simply a function property on your object.

One other core feature of class-based development, getters and setters, are also supported in ES6. This makes it much more obvious what a method is supposed to do:

class AnimalES6 {
    constructor(name) {
        this.name = name;
        this._age = 0;
    }

    get age() {
        return this._age;
    }

    set age(value) {
        if (value < 0) {
            console.log("We do not support undead animals");
        }

        this._age = value;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();
lionES6.age = 5;

Pretty handy, right?

But we can see here a common caveat of JavaScript: the “not really private” private member (_age). I wrote an article some time ago on this topic.

Thankfully, we now have a better way to do this with a new feature of ECMAScript 6 : symbols:

var ageSymbol = Symbol();

class AnimalES6 {
    constructor(name) {
        this.name = name;
        this[ageSymbol] = 0;
    }

    get age() {
        return this[ageSymbol];
    }

    set age(value) {
        if (value < 0) {
            console.log("We do not support undead animals");
        }

        this[ageSymbol] = value;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();
lionES6.age = 5;

So what’s a symbol? This is a unique and immutable data type that could be used as an identifier for object properties. If you don’t have the symbol, you cannot access the property.

This leads to a more “private” member access.

Or, at least, less easily accessible. Symbols are useful for the uniqueness of the name, but uniqueness doesn’t imply privacy. Uniqueness just means that if you need a key that must not conflict with any other key, create a new symbol.

But this is not really private yet because thanks to Object.getOwnPropertySymbols, downstream consumers can access your symbol properties.

Continue reading this article on SitePoint
1 Like

Thanks for sharing this article - a good introduction :slight_smile:

Having not looked at ES6 much before now, Symbols were a strange concept with their use-case not clear.

Axel Rauschmayer describes them in detail (as he always does) here.

Worth a read, but to summarise a couple points here - as no two Symbols are equal they’re a good fit for:

  • making sure property keys on the same object never clash
  • true constants in switch statements

Cheers.

I’m interested to see what your argument would be for using TypeScript over Babel or Traceur, which both seem like much more direct interpretations of the ES6 spec than TypeScript.

2 Likes

I love JavaScript and where it’s heading, imo, Typescript and all that jive is just… well Jive. To each his own though.
I will stick with ES6 and Babel. Not a big fan of Typescript or thatscript, (I have to admit, coffeeScript did influence ES6, and I did come to enjoy using it and then fell out of that phase Tl:DR)
Keep the standards standard and keep pushing forward to a unified web.

1 Like

This article convinced me to use babel :smiley:

2 Likes

Great article. I also found very helpful the following video from James Shore.

you can upgrade to Windows 10 and test with Project Spartan’s rendering engine there

or use almost any browser other than IE (edit - I was wrong on this one, class keyword is supported only in Spartan and Firefox 39, as it turns out)

… but it assumes you know about prototypical inheritance …

Mother of all misunderstandings when it comes to JavaScript. I support adding syntastic sugars, but you can’t go and pretend that JavaScript isn’t prototype-based language. Developers should learn about prototypical inheritance - it’s like saying cars with manual gearboxes are broken (but you just don’t know how to operate them)

Javascript’s factory pattern provides simpler vanilla inheritance without have to use compilers like Babel or rely on complex extending functions.

More info:
Factory constructor pattern
Common Misconceptions About Inheritance in JavaScript

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.