Why do ES6 Classes exist and why now?

In 1995, Sun Microsystems started developing a new language internally called Oak. It bore a striking resemblance to C++, most notably in the use of classes. A class was a fairly radical concept at the time; it packaged up modules of functions that had previously been in loose libraries, and then let programmers create “objects” (or more properly instances) of these classes that kept data self contained within the instance.

This meant that if you wanted to define a character for a role playing game you’d define a class, then use a special constructor to instantiate the class with specific data, something like this:

Once the class was defined, you could then create an instance of the character:

This bound properties and functions to the newly defined object. It also required that everything had a type to tell the compiler the expected input and output types (whether simple types such as strings or complex types such as Game Characters).

When Oak was finally released, Sun decided to change the name to Java, and one of the most heavily used languages in the world today was born.

About two miles down the road, Netscape had been dealing with a somewhat different problem. Their original Netscape Navigator browser had become explosively popular, but up until this point, the content involved was static. CGI gateways had been developed that could parse a URL, but there was no real way to change URLs from static content. Consequently, some basic form controls were added in, which opened up this possibility, but it relied completely upon the controls themselves. Adding support for a scripting language would make this combination of browser and form controls considerably more powerful.

The Netscape team regularly talked to the people at Sun (they were practically on the same street), and the idea took root of creating a simple scripting language that shared a lot of the syntax of Java. Not surprisingly, this became JavaScript in 1996. However, one fateful decision was made by the Netscape developers. They needed to get the browser out quickly, and so the lead engineer at the time (Brendan Eich) put together a language built upon prototyping that could be produced quickly, rather than trying to build a full type system, and so it was decided that strong type checking would not be a requirement.

Instead, they added a special extension to functions called a prototype. If a function did not have a return statement, when it was invoked it would return a prototype. In conjunction with the new keyword, this prototype would generate a dictionary object.

The decision to go with weakly typed variables and prototypes would reverberate through the history of the two languages, even once the browsers themselves became immensely more powerful systems that could have readily supported strong typing and inheritance.

Classes come to ES6

Two decades later, classes are being re-introduced into Javascript.  Ironically, the class keyword has been around from practically the beginning as a reserved word, but was not implemented as such until the Ecmascript 6 iteration.

There are a number of sound reasons for the inclusion at this time:

  • The primary creators of Javascript engines – Mozilla, Google, Microsoft and Opera, have reached a consensus to work towards a unified scripting platform in ES6. This has made it easier to start creating forward looking code, rather than trying to maintain backward compatibility via complex shims.
  • There was a realization made that many people in the Java, C++, C# and Objective C communities, which collectively fuel the development of most standalone applications and mobile apps, found the prototype syntax confusing and non-intuitive. By making a transition to a more traditional class like structure, Javascript would better align itself with most of the contemporary computer languages.
  • Certain applications have a higher need for type checking than Javascript can natively supply. This becomes especially true when complex inheritance chains are involved.
  • At the same time, there are few good reasons (beyond tradition) that Javascript could not implement a class like structure.

At the same time, there has been an emerging consensus that classes in ES6 should be syntactically backwards compatible with existing Javascript capabilities – it shouldn’t break anything.

As it turns out, there are a few fairly critical aspects of classes (such as private member variables or functions) that would in fact impact negatively on the language, and as such these capabilities were pushed off to a later version (either ES7 or perhaps an interim 6.1 release that would also include adoption of import/export capabilities, which are only sporadically supported currently in most implementations).

That this means in practice is that ES6 classes are a way to both testing the water and getting people to explore the notation before these final features make their way into the next iteration of Javascript.

Anatomy of an ES6 Class

At their simplest, classes in Javascript look vaguely like classes in Java:

Unlike with Java, the constructor (the function used to initialize the creation of an instance of a class) is explicitly called out as a function, rather than simply echoing the name of the class. A class can have only one constructor (Java can have any number, provided each has a different signature of parameters (different numbers of parameters, or parameters of differing type).

However, this isn’t as big a limitation in Javascript as you may think, both because Javascript doesn’t have restrictions on parameter types (the same parameter could be a string, a number, an object or a class, for instance) and because with ES6 you can also use rest parameters to convert the parameter set into an array. For instance, the above constructor could be written

where …arr indicates that all arguments after the named ones are contained in the arr array.

You can also use default parameters  and destructuring:

This approach involves passing in two parameters – a single character name and then an array that gets destructured and assigned to defaults if they aren’t otherwise set. You can also just pass these directly:

In this case, the “default” character will be male, human and a fighter.

The this keyword has occasionally been a problem with older JavaScript, because it could represent the containing object, an associated method or a function thrown up asynchronously as part of a promise sequence. Within classes, the this keyword is lexically bound to the class instance exclusively. Thus,

means that the variable named charName is bound (using the this keyword) to the instance (such as gc2, above).   Because this is now consistent in its meaning (within classes), it should cut down on debugging which version of this this this is.

Note also that the function keyword has become scarce. This has been a deliberate design decision that also drove the development of Arrow functions in ES6. The term tended to clutter up older Javascript programming without adding much real information, and while under the hood Javascript is as functional as ever, the goal is to streamline the language, making it clearer to read and write, and as such function may be going the way of the dodo.

A Lack of Privacy

One obvious difference between Java vs. Javascript classes is the lack of public/private keywords. The reason for that is simple – Javascript has no notion of variable privacy.  You cannot create true private variables in ES6 classes, nor can you declare a class public or private. Put another way, you cannot encapsulate private data within functions from within the construct of the class itself.

This may be a temporary state of affairs – ES7 will almost certainly introduce a private keyword.

It is possible to create a “working” private variable through the use of weak maps (this is also the more or less “approved” way of doing things). Weak maps are new features of ES6, just like the strong Map object is. In both weak and strong maps, you can create a hash-map where the key (the first argument) can be an object, not just a string or symbol. For instance, if you create two instances of game character:

The notation is somewhat non-intuitive – in effect, the property associates an object with a value – but this way different instances of a class could use the same property map just by passing in the corresponding this object. Such private variables would then be used within classes as follows:

These variables appear to be private – you cannot directly access them from an instance of the class. Of course, if you have access to the initial weak maps, you can still retrieve the associated values given the instance variable itself, so these aren’t truly private. For that, you will need to wait until ES7.

Methods, Getters and Setters

Suppose, in this game example, that all characters start out with 20 “health” points. When they reach 0 points, they die. However, a priest can invoke their revive() method to bring that up to their full health, though at the cost of one of their three lives. When a character uses up all of their lives, they can’t be revived.

This is a case where both methods and getters/setters come in handy, and ES6 classes support both of them. The following illustrates how such a set of conditions would be implemented:

Here, the constructor has been simplified – changing most of the properties directly doesn’t really make much difference, so charName, gender etc. are now direct properties of the GameCharacter class. The two new properties – health and lives – are a little more complex, because they are somewhat intertwined and are also constrained. A character can never have more than twenty health or three lives, can never have less than zero lives or health and when a person has less than zero points they go back to zero points, but lose a life.

A character with 0 health is “mostly dead” (to borrow from Princess Bride) – they are considered dead, but a healer can heal them and bring them back without losing a life. Falling below zero is bad, however, because then they lose a life, though they are then restored to zero health. This subtlety also insures that once a person has fallen below zero points, they don’t just bleed out lives. (It also points to a nasty side effect of the revive() spell – when cast on a person who is still alive, they are restored to their full health, but they still lose a life. There is no such thing as a free lunch.)

The hurt(), heal() and revive() functions exist primarily to encapsulate the hidden health variable. Hurt passes damage to a character, heal restores damage. They are methods because such methods can be extended. For instance, a character may be immune to certain types of attacks, or be more sensitive to them than the scores would suggest – such as being allergic to rodents of unusual size), but would require the thing causing the damage be passed as an object.

The example given above contains three getter functions. These use the get keyword and take no arguments:

Notice that the public property isRevivable doesn’t have a corresponding private property – rather, it is an indicator of a state (lives > 0). Same holds for the isAlive property This separates the internal logic of the state from the logic of the model.

Historically, setters are used fairly seldom in Javascript (although getters are fairly common).

Static Methods

Static methods in Javascript provide information or perform some utility function on the class itself, rather than a class instance.

For instance, a common requirement for an instance is to get the name of its class. The following also hints at the relationship between a class and its instance. If you define a class as a constant, i.e.,

then ES6 will automatic create a static getter method called name, that can be invoked either on the class itself or on the constructor of the class’s instance:

the static getter name() will return an empty string. As the usage in the line above isa more advantageous when debugging applications (especially if you are using a console where the normal class form defines the class as a constant), it may make sense to declare the getter explicitly:

This kind of approach may also be useful for creating constants that all instance of the class will use. For example, there is an upper limit to the number of health points a given character can have (20 points in the example). You can use static getters to make this a constant that can then be adjusted as appropriate in the class definition, rather than making this an inline expression.

Again, it’s worth noting that within the class static methods are being called not on the this keyword, but on the class itself, e.g, GameChar2.MAX_HEALTH).

In ES7, it’s likely that constants will be definable at the class level itself rather than using getters.


Way back in 1978, Bjarne Stroustrup, creator of the C++ language, outlined what he believed were the cornerstones of object oriented programming:

    • Encapsulation: Objects should hold their state internally, and the interface (the set of constructors, methods and properties) should serve to hide the internal workings of objects.
    • Inheritance: It should be possible to derive more complex interfaces from less complex ones through an explicit extension process, such that the child component inherits it’s interfaces from the parent then builds on them.
    • Polymorphism: If a child object inherits a method from a parent object, the child should be able to extend the method in question to more accurately reflect the child’s perspective.

Curiously enough, Javascript actually satisfies none of these conditions. It is possible to create extensions to functional classes, but to do so requires a fairly deep understanding of how prototypes works. Indeed, usually inheritance is simply not done, because it is easier to just instantiate a new object and copy it.

ES6 makes inheritance much easier to do in Javascript, easy enough that it will most likely make prototype inheritance a distant memory quickly.

Consider again the base GameCharacter class, with a template literal toString() method:

Suppose that you wanted to create two new classes, the first a warrior class who has a set of weapons, the second a mage class who has a set of spells. This can be accomplished as follows:

The extends keyword indicates the parent class in the inheritance chain, with both Warrior and Mage inheriting from the base GameCharacter class. Javascript, like Java, has learned to just say No to multiple inheritance, given that it adds a layer of complexity that is usually more trouble than it’s worth.

The super keyword is somewhat analogous to a this keyword for the parent class. When the Warrior constructor calls

super(charName, gender, species,”warrior”,wealth);

it is in fact calling the constructor for the GameCharacter class. Note here that a string literal – “warrior” – is passed in as an argument, setting the vocation property at run-time. This is the reason that the Warrior constructor has a different set of arguments than the GameCharacter constructor does:

The new constructor creates a new array called weapons that are unique to the Warrior class. Similarly, the Mage class creates a new array for the spells that a mage would use. This distinction gets reflected in the toString() functions, which are similarly redefined:

Note that there’s nothing stopping you from completely redefining the toString() function rather than using the GameCharacter toString() method as a foundation:

Subclasses and Superclasses

There are a few additional methods that come in handy when working with inheritance.

But Wait, There’s More …

Classes in ES6 are complex, controversial, and involve a shift in thinking about the very DNA of the language. There are aspects of classes that the author deliberately avoided, including dynamically generated methods and the use of iterators and generators, all of which get into the deeper aspects of ES6 programming. What was also deliberately avoided until now was whether in fact these changes are good for the language.

I think they are. When Javascript was first created, it had (rightly) gained a reputation as a toy language – there were a limited number of things that could be done with it, it was slow and cumbersome, and it was seen as being lowest common denominator programming.

That’s changed dramatically in the last twenty years. In the news lately was an article about the lawsuit brought by Oracle against Google claiming they violated the terms of the agreement on the use of Java (bought by Oracle from Sun about a decade ago) in the development of the Android operating system. Google ultimately prevailed (this time), but the writing is on the wall – Java could still be seen as being tainted by encumbrance. I expect one immediate consequence of this will be the shift away from that language to one that is an open standard for Android use, one that Google already has heavily invested in, and one which is robust enough to handle modern programming needs. Javascript is that language.

ES6 classes represent a major shift forward in making this happen. While the language may lose some of its idiosyncrasy, it is moving towards being one of the most universal of all computer languages, and contemporary class inheritance will become a requirement for that adoption. Personally, I find the new ES6 classes easier to work with than more traditional Javascript, along with features such as arrow functions and destructuring that can significantly simplify a lot of the callback hassles that the language has been saddled with.

At the same time, Javascript developers have become much less inclined to create heavy inheritance structures, and its likely that Javascript best practices will better suit a useful middle ground, one where you don’t have a need for thousands of interconnected APIs but can get away with far shallower class trees.

Perhaps 2015 will be seen as the emergence of the Classical Period of Javascript. We’ll see.

The following code pen explores a number of the same themes for a sample RPG Generator

See the Pen RPG Generator by Kurt Cagle (@kurt_cagle) on CodePen.

Kurt Cagle
Kurt Cagle (kurt.cagle@gmail.com) is an author of twenty books, blogger, and software architect, and is available for consulting or hire. He lives in Issaquah, WA with his wife, daughters, and cat.