Classes in JavaScript: Exploring the Implementation in Chakra

The Windows 10 Technical Preview November build and RemoteIE includes many changes and additions to Internet Explorer from the next edition of the JavaScript standard, ECMA-262 6th Edition (ES6). ECMAScript 6 will be a substantial advance for JavaScript language, and currently the technical preview is the most compliant implementation. This post explores where ECMAScript is going and gives a peek into the implementation of a new language feature: ES6 Classes.

ECMAScript Evolution to Meet the Advancing Web

The web has evolved substantially in the 5 years since ES5 was ratified. Apps today are becoming more complex and demanding. Microsoft, working with the ECMA working group - TC39, has been hard at work on the next substantial update to the language. The 6th edition of ECMA-262 (ES6) is perhaps the biggest update to the language in its history.

The order we implement ECMAScript proposals depends on a few principles including whether it is necessary to ensure interoperability with other engines, enables new types of apps, provides developers with compelling value beyond saving keystrokes, has a stable specification has multiple reference implementations, and has a comprehensive test suite (called Test262) to ensure interoperability. Overall we do our best to make data driven decisions and deliver features that will give the most value to developers. Let us know what you find most valuable on User Voice or Connect!

ES6 Classes

Many features in ES6 aim to reduce time writing code and increase expressiveness of common patterns in JavaScript. Because they do not add new fundamental capabilities to the runtime and essentially simplify the boilerplate code, these kinds of features are collectively referred to as ‘syntactic sugar’.

ES6 Classes in JavaScript is one such feature and remains one of the most commonly requested features on status.modern.ie.

To those of you familiar with object-oriented languages, classes should be a familiar concept. However classes in JavaScript are a little different from other languages. With ES5, class functionality is commonly implemented using functions, prototypes, and instances. For example:

By contrast, the ES6 version below is much more readable and concise. For programmers from a classical inheritance background, the functionality is easier to understand without in depth knowledge of how JavaScript's prototypal inheritance model works.

Now that we’ve seen a simple example of what the syntax looks like, let’s have a look at the other syntactic features of ES6 classes.

The example includes a basic class construct with instance, static, getter and setter methods. A class constructor is a special method which can be customized or omitted. Omitting a constructor generates the default constructor which merely forwards arguments to its super as shown above. Classes provide inheritance through the extends keyword. A subclass has the super keyword, which allows use of the superclass properties and methods.

Implementation challenges

In a previous post, we explored the architecture of the Chakra engine in detail (see ‘Announcing key advances to JavaScript performance in Windows 10 Technical Preview ) using the diagram below. The parts requiring the most modification to support classes are highlighted in green.

Parts of the Chakra engine requiring the most modification to support ES6 Classes

When implementing new language features, syntactic sugar features often map directly to older language constructs, as shown in the first code example. Classes are no exception. Underneath, classes use the same function, prototype, and instance constructs that have always been available, and Chakra uses these constructs to implement classes.

Not all of the sugar maps to pre-existing language features, however. The super keyword is one example. super ended up taking longer than the rest of classes due to its complex usage scenarios. These uses are not always apparent to the end user, but part of the fun of being a compiler developer is to come up with the corner cases that break things. For example, the use of eval() in a class makes the implementation more complex:

In the example above, both Child methods require a reference to Parent to allow execution of super(). In the case of method(), the parser is able to detect the use of super() at compile time. Chakra marks method() as having a super keyword reference during parsing, so when it reaches bytecode generation it knows to generate special bytecode to make a reference to Parent available. At runtime, this reference becomes available as Chakra consumes the bytecode at the beginning of the function, performing checks on whether it is valid to use super at that location and point in time.

In the case of methodEval(), there is a call to eval() which drastically changes the implementation. Chakra knows this is a class method, but until it executes the eval(), it has no idea if there is a super reference, or if there will ever be a super reference. This poses a design problem that we felt had two options:

  1. Should Chakra maintain a reference to Parent that is available in case it is needed by super somewhere inside the eval(), or
  2. Should Chakra wait until it is somewhere in the eval()to fetch the reference?

The first option, pre-emptively loading super, adds unnecessary bytecode to the method which could adversely affect performance.

The second option, to fetch the Parent reference when Chakra needs it, sounds simple at first. However it can’t predict the complexity of the eval(). For example, Chakra could be several levels deep in function calls in the eval(), or perhaps be in several levels of the eval()itself. Finding the Parent reference at this point could be more costly than if it had been stashed it away previously.

There isn’t necessarily a correct answer here, and the answer may change as we learn more about how programmers use super. In the current technical preview, we implemented super by pre-emptively loading the reference in situations where the trade-off in performance is justifiable. For example, using super in a subclass method is more likely to happen than in a top level class method (regardless of whether the use is valid or invalid.)

In the time since the release of the technical preview, the ES6 specification has been updated to allow super in more places. Being on the bleeding edge of implementation as standards evolve often means revisiting implementations, and the ES6 specification is no different.

Classes are a core part of the ES6 language feature set. We anticipate they will help new JavaScript programmers learn the language more rapidly when they have experience with other object oriented languages. We also anticipate that many seasoned JavaScript programmers will welcome the terseness.

Moving forward with ECMAScript

While ECMAScript 6 makes great progress, both the runtime capabilities and expressivity will continue to evolve. ECMAScript 7 should arrive more rapidly than any previous ECMAScript version because it and future editions of the standard will be moving to a more regular “train model”. In this model, proposals are built in isolation from one another and move through stages of ratification as they mature. Eventually, a proposal may make it on to a “train”, where the proposal is integrated with the other proposals on the train to form the final document that will be ratified. This model is useful for language standards because we get cohesive, complete specifications developed in an agile fashion that we don’t have to wait years for. It also gives some flexibility to make sure we get a design right. It’s a lot easier to take a bit more time when it means shipping in the specification next year rather than five years from now. For example, when the committee got substantive negative feedback on the subclassing built-ins machinery, it was relatively easy to pull it out of ES6 and get it ready for making the ES7 train. (You may also be interested to know that we were analyzing a subclassable built-ins implementation to complement classes, but as a result of the change we could not include it in this release.)

This model should also be much friendlier to JavaScript developers because new features come to the language yearly rather than every half decade. Also, browsers can pick up smaller chunks and begin implementing them sooner in the process, which should help get interoperable implementations more rapidly. The end result of this new process should be substantially increased pace of improvements to JavaScript.

Summary

Classes are available today in the current Windows 10 Technical Preview, and you can also play with them using remote.modern.ie. We can’t wait to hear about how you will be using the feature in your ES6 code. We’re planning to deliver more articles in the future about other ES6 features. In the meantime, feel free to join the comment discussion, reach out on Twitter @IEDevChat, or on Connect.

Tom Care (@tcare_), Brian Terlson (@bterlson) & Suwei Chen

Chakra Team