The Future of JavaScript - Pro JavaScript Techniques, Second Edition (2015)

Pro JavaScript Techniques, Second Edition (2015)

11. The Future of JavaScript

John Resig1, Russ Ferguson1 and John Paxton1

(1)

NJ, United States

We have taken quite the tour of JavaScript in this book. It is clear that JavaScript is a language in transition. From its humble beginnings as something of a toy language, JavaScript has ascended to the level of an enterprise-critical language. In the process, the seams have begun to show and, frankly, come somewhat loose. When developers from more mature languages come to JavaScript, they often marvel at what we have accomplished, given JavaScript’s limitations. They are given to wonder at how the language got so far, and whether it will improve in the future.

JavaScript’s “graduating class” of 1995 has some of the brightest lights of programming: Java, Ruby, PHP, and even ColdFusion are all thriving languages to this very day. Many developers would say that JavaScript’s classmates are far ahead of JavaScript. Yet many of them are taking their cues from JavaScript, looking at its use of prototypes, its implementation of functions as first-class citizens, its flexible style and more as inspiration for their own new features.

What does the future hold for JavaScript? Where does it go from here? Thankfully, unlike our own dim and obscured futures, the future of JavaScript has a road map and even some evolving specifications. ECMAScript 6 will probably be a fully adopted (if not quite implemented) standard by the time this book is published. ECMAScript 7 is already in development and under debate. To deploy a cliché, JavaScript’s future looks bright indeed.

Let’s take a look at what lies ahead for JavaScript. We will discuss a little bit of how we got here and where we are going, looking at the standards process. Then we will look at what we need to do to use JavaScript with our current set of tools. But the bulk of this chapter is concerned with the details of ECMAScript 6: the language features you can expect to be working with over the next few years. We will even hint at some of the distant future, which may or may not come to pass...

Of course, we do not have the space to go over the entirety of the ECMAScript 6 specification. We have worked to pick and choose the more useful, better defined, and most interesting features to look at.

The Once and Future ECMAScript

We should start with what we know. The European Computer Manufacturers Association, now known as Ecma International, is the body that oversees the standard to which JavaScript adheres. One could write a book on how this came to pass, and it’s not really important for our jobs as JavaScript programmers. What is important is that a group within Ecma, Technical Committee 39 (TC39), has taken up the banner of JavaScript standards and is promulgating updates. Perhaps more important is the fact that various computer manufacturers, software companies, and other interested parties are vested in the success of this standard. We, as the JavaScript community, have a viable system for directing the future of the language. This should help to head off some of the rancorous differences we have endured in the past, as well as to streamline the process of positioning JavaScript as an effective, enterprise-class language.

The process of wrangling JavaScript into an effective and effectively governed standard has been a long one. Most of the features that we think of as “standard” JavaScript came from ECMAScript version 3. This version comes from the era when Ecma was still playing catch-up with the browser makers. Although there were efforts to create a fourth version of the ECMAScript standard, they were ultimately abandoned. Ten years after version 3, version 5 of the standard was offered in 2009. This aimed to set a new status quo, catch up to the intervening changes in the landscape, and clarify the many ambiguities from version 3. The standard was widely accepted and helped to clear the path for Ecma’s resumption of its duties managing the JavaScript standard.

Even now, wide acceptance of ECMAScript version 5 is not a given. Internet Explorer 9 was the first version of IE to implement the standard. A significant portion of the world still uses IE 8 and earlier. The challenge for Ecma International and TC39 has not so much been setting the standard, but getting the audience to upgrade to the standards once they are offered.

There was some confusion about what the next step would be after version 5. In the end, two tracks were taken: there was a 5.1 standard, which brought ECMAScript in line with the International Organization for Standardization’s specification for ECMAScript (a long and boring story in itself). And there would be a new version of the ECMAScript standard, version 6, often referred to as ECMAScript Harmony (the name comes from a variety of proposals over time to harmonize various standards of JavaScript, ECMAScript, JScript and so on, as well as the original code name for ECMAScript version 4).

As programmers, we are most interested in the new standard and what it will enable us to do. The ES6/Harmony proposal should be finalized by the middle of 2015. So let’s leave the world of standards behind and talk about how we can work with Harmony today.

Using ECMAScript Harmony

Unlike ECMAScript 5, which had a long life as a de facto standard before it became a de jure standard, Harmony is leading, rather than following. This means that current (as of the writing this chapter) implementation of Harmony is spotty. We need a few tools to manage Harmony’s various states of implementation. First, we will need resources to tell us which browsers implement which aspects of Harmony. Second, we will look at how to enable Harmony in those browsers, and third, we will examine some software tools that can transpile our ES6 code into ES5-compliant code. After that, we should be able to dive into some of the features of the standard that are either in wide use or widely expected to make it into the final standard.

Harmony Resources

TC39 maintains a wiki that allows the public to track the state of the Harmony proposal, at http://wiki.ecmascript.org/ . Two pages are of particular interest: the requirements/goals/means page and the proposals page. The requirements page lists the methodology that guided the development of ES6. While not critical in our understanding of the language, it does inform us about why certain decisions were made, in terms of the goals and means defined for the Harmony project. For example, Harmony has a proposal for proper block scope, but implements it through the addition of a keyword (let) instead of simply redefining the way JavaScript interpreters work. The proposal itself follows the first two subpoints of the first goal: Be a better language for writing complex applications and libraries. But the implementation follows the fourth goal (keep versioning as simple and linear as possible) as well as the first means (minimize the additional semantic state needed beyond ES5). You can find the requirements, goals, and means of the Harmony project at http://wiki.ecmascript.org/doku.php?id=harmony:harmony .

The other important page for the Harmony process is the proposals page. This tracks the various proposals made for ES6 and the state of each one. The call for proposals was closed in 2011, so the page should not see additions. For the most part, you should see the existing proposals, and, occasionally, proposals that have been removed from the specification. You can find the proposals page at http://wiki.ecmascript.org/doku.php?id=harmony:proposals .

Specifications are wonderful as reference documents, but they are sometimes lacking in implementation details. We would like to have a reference that tells us the state of ES6 implementation across browsers and other JavaScript engines. Luckily, we have two such pages. The best page is by the noted JavaScript developer who calls himself kangax (Juriy Zaytsev), and is known as the ECMAScript 6 compatibility table. You can find it here: http://kangax.github.io/compat-table/es6/ . The compatibility table breaks down ES6 implementation by feature and checks it against most modern browsers, desktop and mobile, as well as other JavaScript implementations like Node.js. The tests are somewhat simplistic, usually focusing on existence of a proposal, not the functionality thereof and not the implementation’s conformance with the proposal. Nonetheless, it’s a great starting point. kangax also maintains compatibility tables for ES5, the coming ECMAScript 7 specification, as well as nonstandard features like __defineGetter__ or the caller property on functions.

Thomas Lahn also maintains the ECMAScript matrix, which tracks implementation of ECMAScript standards across current versions of JavaScript engines. You can find his efforts at http://pointedears.de/es-matrix/ . Lahn’s approach is somewhat different from kangax’s. Lahn is interested in current JavaScript engines, so he only tracks JavaScript, V8 (Google Chrome’s engine), Opera, and a few others. Contrast this with kangax, who is more browser- and software-oriented. Also, Lahn’s matrix tracks all of ECMAScript, at least through version 6, so you can see thelet keyword evaluated alongside arrays and for loops. His approach is more thorough, but also leads to a larger table (and a somewhat more slowly loading page). Nonetheless, this, like kangax’s compatibility table, is an indispensable resource for the professional JavaScript developer.

Working with Harmony

There are four states in which a browser can work with the features of Harmony.

· The browser, particularly evergreen Chrome and Firefox, may have a ready-to-go implementation of a Harmony feature with no special effort needed on the part of the programmer. Very few features work this way as of publication time, though.

· Most browsers will require you to “opt-in” to using Harmony features. We will talk about this in detail shortly.

· If your browser does not have a feature enabled or implemented (or it is not implemented properly!), you may consider using a transpiler, which will let you write ES6-level code and then output ES5 that can run on the engine of your choice.

· Alternatively, you could use a polyfill for a particular feature from ECMAScript 6 you would like to use.

Obviously, the first state requires little explanation, so let’s talk about the second state. Both Chrome and Firefox have their own quirks in working with ECMAScript 6.

In Chrome, you will have to go to the chrome://flags URL, which allows you to enable experimental features. Specifically, you will need to enable chrome://flags/#enable-javascript-harmony. Now, keep in mind that doing so may change Chrome’s behavior in many circumstances, and could have odd results in the way Chrome renders certain pages. And the change to the state of enable-javascript-harmony is persistent. If you would prefer, instead, to change only at startup of a specific session, run Chrome from the command line with the --javascript-harmony switch. Additionally, some examples require running JavaScript in strict mode, which can be managed at the code level.

For Firefox, you do not have to change any settings when you start it up, but you may have to change your code. In general, Firefox will require you to label your code as being different from standard JavaScript. Add the type attribute to your script tags, and set the type toapplication/javascript;version=1.7, This should enable most Harmony features. If there are any additional changes needed to run Harmony code, we will note them with the specific feature they enable.

Interestingly, Internet Explorer requires the least configuration to run ECMAScript 6 code—“least” in the sense of “none.” On the other hand, IE 10 has only four items in the specification implemented. Internet Explorer 11 has a total of 12 items in the specification implemented, but it lags far behind Firefox and Chrome. It can be said that while IE 11 has not implemented much, what it has implemented, it has done so simply.

Transpilers

The third option for working with ECMAScript 6 is a transpiler. A transpiler takes code written for ECMAScript 6 and cross-compiles it into ECMAScript 5-compatible code. There are a number of different transpiling tools. Addy Osmani maintains a list at GitHub of transpilers and polyfills. You can view the list at https://github.com/addyosmani/es6-tools . You can see from that link that there are a number of transpilers and polyfills. We will demonstrate using the Traceur transpiler to take some ECMAScript 6 code and run it in a browser using ECMAScript 5. Traceur is among the more popular transpilers, and it is also among the more frequently updated.

The easiest way to use Traceur is to load it via Node.js. Similar to the way we used Node as our own JavaScript VM in the chapter on JavaScript tools, we will use Node here to load up additional code via NPM. Start by loading Traceur

npm install traceur

This installs the Traceur transpiler, currently on version 0.0.72. Recall that you can use the -g option if you want Traceur to be globally available. Either way, you will probably need to update your PATH variable to include Traceur. On Windows, in your node_modules folder you will find the following file: .bin\traceur.cmd, which is a Windows batch file wrapped around running Traceur with Node.js. You should be able to run Traceur directly if you add the node_modules\.bin directory to your PATH. Check that Traceur is on your path by running traceur--version, which should return the version number or an error message if Traceur can’t be found.

You can run Traceur against existing ES6 code. Invoke traceur from the command line and pass it, as an argument, a JavaScript file that contains ES6 code. (You could, of course, pass it a file that only has ES5 code in it, but where’s the fun in that?) Traceur will run your code and output anything it prints to the console.

Consider an example of the new class syntax in JavaScript. Quickly, ES6 will allow you to create classes, though the code is just a syntactic wrapper around the functional style of type. The syntax is easy to read and figure out, so let’s use it for our Traceur example, shown in Listing 11-1.

Listing 11-1. ECMAScript 6 Classes with Traceur

class Car {

constructor( make, model ) {

this.make = make;

this.model = model;

this.speed = 0;

}

drive( newSpeed ) {

console.log( 'DEBUG: Speed was previously %d', this.speed );

this.speed = newSpeed;

console.log( 'DEBUG: Speed is now %d', this.speed );

}

brake() {

this.speed = 0;

console.log( 'DEBUG: Setting speed to 0' );

}

getSpeed() {

return this.speed;

}

toString() {

return this.make + ' ' + this.model;

}

}

var honda = new Car( 'Honda', 'Civic' );

console.log( 'honda.toString(): %s', honda.toString() );

honda.drive( 55 );

console.log( 'The Honda is going %d mph', honda.getSpeed() );

As you can see, we create a type, Car, and define three properties (make, model, and speed) as well as a few methods which are wrappers around a property (brake, drive, getSpeed) or conveniences (toString). Nothing too crazy.

This code will not run in any modern browser. We know, because we tested it. Also, if you look at kangax’s compatibility tables (as of publication time), you will see that classes are not implemented by any of the major browsers. So this code is a good choice for experimentation with Traceur.

If you were to save the code in a file (classes.js in the folder for this chapter, as a matter of fact), you could run it with Traceur:

traceur classes.js

Your output would look something like this:

A978-1-4302-6392-0_11_Figa_HTML.jpg

As you can see, Traceur handles the code just fine. Behind the scenes, Traceur compiled the code down to ES5, and then simply ran the code using Node.js itself. No big deal.

But what about browsers? Well, we have two different options. We can use Traceur to generate output files that run in browsers which support ES5. Alternatively, we can use ES6 code directly in the browser and have Traceur transpile it live. Start with transpiling output. Use the --outoption to tell Traceur to generate output to a file of your choosing. The output file is not a standalone file. You can run it with Traceur, obviously, but you cannot simply include it into an HTML page. You will need to load up the Traceur runtime first, and then the script you want to run. Listing 11-2 is an example of a Traceur HTML shell.

Listing 11-2. HTML Shell for Traceur

<!DOCTYPE html>

<html>

<head>

<title>Traceur and classes</title>

</head>

<body>

<h2>Running Traceur output in the browser</h2>

<script src="../node_modules/traceur/bin/traceur-runtime.js"></script>

<script src="classes-es5.js"></script>

</body>

</html>

Note that we are loading the traceur-runtime.js file from the bin file of Traceur. This file is a distillation of the code needed to run Traceur-transpiled files. (The other, considerably larger, file in the directory is the code for Traceur itself.) Loading Listing 11-2 (available as classes-es5.htmlin the chapter folder) yields results on the console as expected. Perhaps more importantly, it works great for current versions of Firefox, Chrome, and Internet Explorer.

If you wanted to work with ECMAScript Harmony code directly, you could always have Traceur transpile your code live. We can use the original Harmony code from Listing 11-1, and modify the HTML shell from Listing 11-2 as follows:

<script src=" https://google.github.io/traceur-compiler/bin/traceur.js "></script>

<script src=" https://google.github.io/traceur-compiler/src/bootstrap.js "></script>

<script src="classes.js" type="module"></script>

We have switched to using Google’s GitHub repository for Traceur simply because it’s the easiest way to access the second file: bootstrap.js. This file is not included in the NPM install of Traceur. It is also not included in the Bower install of Traceur. So we will refer to it directly. Bootstrap allows you to run Traceur from within a JavaScript context. Also, we are now referring to the file classes.js as being of type module. This is a convention of bootstrap.js, which loads classes.js, via Ajax, as an explicitly ES6 file. Additionally, the type attribute has the side effect of not loading error-provoking code into the browser. As an alternative, you can simply include the JavaScript ES6 code in an inline script block, though you will still need the type attribute set to module.

While in-line transpiling is a fun experiment, we cannot recommend it as a regular way to develop or deploy. It’s extra work that has to be done every time a page loads, work that can be done once by transpiling to an output file. Not to mention that traceur.js plus bootstrap.js plus your code is a much more significant download than traceur-runtime.js plus your code.

Polyfills

Finally, for some aspects of ECMAScript 6, you could load a polyfill into your page. The use case for this solution is a bit narrower, but the application is much broader. Instead of the whole set of ECMAScript 6, which you get with something like Traceur, you can focus on exactly the features you want by using polyfills. But this comes at a cost. It is easy and logical for a polyfill to provide the new methods on the prototypes for String, Number, and Array, or to implement the WeakMap and the Set. On the other hand, polyfills cannot, by their very nature, substitute language features like let, const, or arrow functions. So the set of Harmony features you can access with polyfills is limited.

All that said, for the implementable polyfills, there are quite a number of high-quality implementations. Addy Osmani’s directory of ES6 tools contains a section on ES6 polyfills. Of note is Paul Miller’s ES6-Shim ( https://github.com/paulmillr/es6-shim/ ), which has polyfills for most aspects of Harmony that can be polyfilled. When we go over the features list for ECMAScript 6 later in this chapter, we will note those features that are provided by ES6-Shim.

ECMAScript Harmony Language Features

ECMAScript 6 introduces a large number of new language features. These are features that fill in what have been glaring blind spots in JavaScript (block scope), trim down the syntax to focus on the core of functionality (arrow functions), and expand JavaScript’s ability to handle more complex code patterns (classes, modules, and promises).

Take block scope as an opening example. JavaScript’s odd approach to scope and hoisting (the practice of lifting variable and function definitions to the top of their local scope) has been a stumbling block for new JavaScript programmers for years. It is also a cultural impediment: programmers from “real” languages (whatever that might mean) scoff at JavaScript because it lacks block scope (or classes, or this, or that). Regardless of the substance of this criticism, it is preventing some people from coming to experience the good parts of JavaScript. So let’s address it and move forward.

The let keyword allows you to scope variables to an arbitrary block. Variables scoped with let are not hoisted. Those are the two critical differences between let and var. It could be said that variables scoped with let act the way you would expect most local variables to act (whereas variables scoped with var have some interesting features/capabilities). If you try to access a let-scoped variable outside its block, you get a ReferenceError, much as you would if you tried to access an inaccessible var-scoped variable. It isn’t more complicated than that. Use let in place ofvar when you want block scoping.

As a companion to let, there is also const, which allows you to declare a constant-value variable. For variables declared with const, you should initialize at declaration time because otherwise, what’s the point? You will not be able to change the value of the variable later on! Like let,const variables are not hoisted, and they are scoped to the block they are declared in. The semantics of trying to access const variables are a little different from browser to browser. If you try to modify a variable declared const, your browser will either fail silently or may throw an error of some type. Constants enable certain bits of code to be compiled into faster code, as the JavaScript engine knows that they will never change. Also, when paired with some of the collections we will see later in the chapter, you can use constants to store private data for a class. Seehttp://fitzgeraldnick.com/weblog/53/ for details.

Arrow Functions

Another cateogry of improvements in ECMAScript 6 is the introduction of syntactic changes that simplify certain declarations. One of these changes is the arrow function. Arrow functions serve to give you a shorter syntax for defining functions, particularly in-line functions. Consider this code:

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];

numbers.forEach(function(num) {

// Do something with the number here

});

It’s not unwieldy, but it is a touch verbose. JavaScript has a unique challenge: try to minimize literal code length, while not compiling code. Having to define functions using the keyword function is not light on bandwidth. And the more functions we use, the more times we have to use the word function. It would be nice to have shorter function syntax.

Inspired by CoffeeScript, TC39 introduced a proposal for arrow functions or, more clearly, arrow-defined functions. The previous example can be written like so:

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];

numbers.forEach(num => {

// Do something with the number here

});

Somewhat more elegant, no? Here are the critical parts of the syntax:

arguments => { code }

() => { code } // No arguments

i => { code } // One argument

(i, j) => { code } // multiple arguments

For specifying the body of the function, you may wrap multiple lines of code within a set of curly braces or, if you have only one line of code, you may leave it bare. Thus

x => x * 2

Is the equivalent of

function(x) {

return x * 2;

}

There are a few differences between arrow functions and regular or standard functions. First, arrow functions are always bound to the context in which they are defined. This sounds complicated. You may have run into this problem:

var courseAssignments = {

teacher : 'Stephen Duffy',

canTeach: function(courses) {

courses.forEach( function ( course ) {

console.log( 'Ask %s if he can teach %s', this.teacher, course );

});

}

};

courseAssignments.canTeach(['Greek', 'Latin', 'Theology', 'History']);

When you run this code, you will see output like this:

Ask undefined if he can teach Greek

Clearly not what we want. The context and value of this within the forEach function refers back to the canTeach() function, not to the courseAssignments object. Usually the problem is repaired this way:

var courseAssignments = {

teacher : 'Stephen Duffy',

canTeach: function(courses) {

var that = this; // Store the context of canTeach

courses.forEach( function ( course ) {

console.log( 'Ask %s if he can teach %s',that.teacher, course );

});

}

};

courseAssignments.canTeach(['Greek', 'Latin', 'Theology', 'History']);

Note the code in bold, which highlights a strategy for storing the context of this at the canTeach level, instead of inside the forEach.

Arrow functions simplify this situation dramatically. They are automatically bound to the proper context. You could rewrite the previous code as

var courseAssignments = {

teacher : 'Stephen Duffy',

canTeach: function(courses) {

courses.forEach( course => console.log( 'Ask %s if he can teach %s', this.teacher, course ) );

}

};

courseAssignments.canTeach(['Greek', 'Latin', 'Theology', 'History']);

With the arrow function, this.teacher is automatically bound to the context of this in canTeach, which is what we wanted all along. Terrific!

There are two other differences with arrow functions. Arrow functions cannot be used as constructors. They lack the internal code to act as such. Arrow functions also do not support the arguments object. This is fine because ECMAScript 6 also defines and permits parameter default values and rest parameters, so we will no longer need to rely on the arguments object in functions in general.

Classes

One of the biggest syntactical changes in ECMAScript 6 is the introduction of classes. The keyword class had been reserved in JavaScript since its inception. But there was no implementation behind it. With the rise of object-oriented JavaScript, TC39 acknowledged that JavaScript needed a more syntactically clear way to implement classes and inheritance. At the same time, there was no desire to add yet another way to implement types, classes, or something like classes. The idea was to simplify and to relieve confusion, not create more confusion.

Classes, ECMAScript 6:The ECMAScript Harmony specification settled on using the class keyword as syntactic sugar to implement function-based types. If that reads a bit strange, here’s what actually happens:

· You write a JavaScript class, based on the new ES6 syntax.

· The JavaScript engine compiles it down to a function which defines a type.

· You interact with that class the same way you would a type.

So the semantics of interacting with the class/type and its instances have not changed. And many of the semantics of defining a class/type are the same as well. Let’s go back to our previous example for more detail. Recall how we defined a class in JavaScript:

class [ classname ] {

constructor(arg1,arg2) { ... }

[other methods]

}

Define the class with the class keyword. Include a function called constructor. This is the function that will be called when invoking new [classname] later on. Supply other methods as needed. The other methods will be copied onto the prototype of the function defined as the constructor. Pretty nifty!

Classes will also have two critical features long desired for the object-oriented side of JavaScript: easy inheritance and a super accessor. Inheritance is available via the extends keyword:

class Car extends Vehicle

Within a subclass, you can use super as an accessor to the superclass’s methods and properties. Unlike some languages (Java for instance), ECMAScript 6 doesn’t let you invoke super as a method itself. Rather, it holds a reference to the superclass, similar to the way this holds a reference to the current instance.

Unfortunately, classes are not implemented at all in any of the major current browsers. The class specification will be part of the final specification of ECMAScript 6 released in 2015, and it seems that browser makers are waiting until the final specification to make sure their implementations don’t get anything wrong. In the meantime, the Traceur transpiler handles classes quite well, including extends and super.

Promises

Back when we were talking about Ajax, we discussed a bit of the problem with Ajax-based systems: code flow. When involved with a function that returns results asynchronously, it is difficult to program dependent code. For a long time, there was no concrete, straightforward solution. Most programmers either wrote very long callbacks (which may have, in turn, called their own asynchronous functions, complicating the matter!) or used named functions, but bounced around the call stack. (Not to mention that this method introduces another complication; there are many named functions. Do you manage them with a module? Just a namespace?) Asynchronously running code is at the heart of JavaScript’s feature list, so we needed a better way to manage asynchronous interactions. Enter promises.

A promise is a pattern that helps to manage asynchronously-returning code. It encapsulates that code and adds a layer of management that tastes of event-handling. We can register code with the promise that should run if it returns successfully or unsuccessfully. When the promise does complete, the appropriate code is run. We can register any number of functions to run on success or on failure (very much like event handling), and we can register a handler at any time (whether the promise has completed or not; very much NOT like event handling).

Let’s take a technical walkthrough of a promise to clarify the feature’s operation, as demonstrated in Listing 11-3. Broadly, the promise has two states: pending or settled. The promise is pending until the asynchronous call it wraps around returns or times out or otherwise finishes. At that point, the promise is settled. The state of being settled has two flavors (if you will): resolved and rejected. A resolved promise settled successfully, a rejected promise settled unsuccessfully. As promises are arbitrary (technically, they do not even require asynchronous code!), the definition of rejection or resolution is really up to you.

Listing 11-3. Promises

var p = new Promise( function(resolve, reject) {

// Do some things here, maybe some Ajax work?

setTimeout(function() {

var result = 10 * 5;

if (result === 50) {

resolve(50);

} else {

reject( new Error( 'Bad math, mate' ) );

}

}, 1000);

});

p.then(function(result) {

console.log( 'Resolved with a value of %d', result );

});

p.catch(function(err) {

console.error( 'Something went horribly wrong.' );

});

Admittedly, this is a bit contrived, but it is nonetheless a good simple example of some asynchronous code. At the heart of the matter is the call to setTimeout. Of course, the function we are scheduling for later execution does nothing other than run a mathematical equation. The critical parts are the calls to resolve and reject. The resolve function tells a promise consumer that the promise has resolved (completed successfully). And the reject function does the same for promises that settled unsuccessfully. Note that in either case, we are passing along a value as well. That value will be what the consumer receives in the function that handles the resolution or rejection.

We also have some code here that consumes the promise. Note the use of then and of catch. Think of these as the “onSuccess” and “onFailure” event handlers. To be strict, the signature for then is Promise.then(onSuccess, onFailure). So you could use a call to then directly. In terms of style, though, it is much clearer to have a separate call to catch to handle any errors that come along.

The really clever bit is that we have disconnected the handling of the promise from the state of the promise. That is, we could call p.then (or p.catch) as many times as we want, regardless of the state of the promise. If we are still inside the 1000 millisecond time it took to settle the promise, code registered with p.then will be added to the stack of code to be called when the promise settles. If we are after that 1000 millisecond time period (as we are likely to be), the code will execute immediately. You could imagine some pseudocode inside the promise implementation that works like this:

function then(onSuccess, onFailure) {

if (this.state === "pending") {

addSuccessStack(onSuccess);

addFailureStack(onFailure);

} else if (this.state === "settled") {

if (this.settledState === "success") {

onSuccess();

} else {

onFailure();

}

}

}

The actual implementation has a few more details which we won’t go into here. Nonetheless, this is a useful approximation of what goes on inside a promise when you register code to run when that promise settles.

Promises are the standard, ECMAScript 6 way of managing asynchronous code. Indeed, many JavaScript libraries are already using promises to manage Ajax, animation, and other typically asynchronous interactions. As we will see in the very next section, we will need to have and understand promises to use the Harmony implementation of modules.

If you want to work with promises in the meantime, there are a variety of options. Traceur understands promises and uses them internally to implement modules (and a few other features). Libraries like jQuery, Angular, and a few others have their own implementations of promises, though there are some differences from the official specification. This is particularly the case with jQuery, so be careful. ES6-Shim contains a promise implementation, and the excellent Q library by Kris Kowal has a Q.Promise type which implements the ES6 API.

Our lightweight discussion here has only touched on the promise of promises). There is more to the API and there is more to the type. There are a number of terrific articles on promises on the web, but if you are going to start somewhere, consider this HTML5Rocks article by Jake Archibald: http://www.html5rocks.com/en/tutorials/es6/promises/ .

Modules

Wait, haven’t we seen modules before? Well, yes, when speaking of, say, RequireJS, you can talk about AMD (Asynchronous Module Definition) modules. And if you are in the world of Node.js, you might think of CommonJS modules. The ECMAScript 6 standard seeks to resolve these two different styles of modules into one syntax. It is somewhat successful. Inevitably, there will be complaints from both sides. But in striking a middle ground, using aspects of both standards, TC39 has provided programmers with a clear path to follow. If you want to stick with AMD styled modules, or prefer CommonJS style modules, someone will eventually write a transpiler... possibly you.

That having been said, let’s look at the implementation of ES6 modules. We will not spend time comparing and contrasting ES6 modules with AMD or CommonJS modules. That is an exercise for another chapter of another book (or for an excellent blog post by Alex Rauschmeyer:http://www.2ality.com/2014/09/es6-modules-final.html ). Instead, we will work with modules to look at what they provide us.

The idea behind the module is simple: we want a safe, encapsulated namespace inside of which we can define data and functionality. Then, at our discretion, we can make some or all of that data and functionality available. Reusability is the main use of a module, allowing us to define functionality once and use it anywhere we need to. Listing 11-4 shows how modules implement these requirements.

Listing 11-4. A Module Defined

export const schoolName = 'Mickey Kullen Memorial High School';

export const firstSport = 'Basketball';

export function getPrincipal() {

// Probably a call to a server in the real world

return 'Jason Franzke';

}

export function fgPct(shots, baskets) {

return baskets / shots;

}

As you can see, we export the public API for the module via the export keyword. When using export on a member-by-member basis, we can export constants, regular variables, and functions. If we prefer, we can list all of the exports as the last line of the file (even changing their names in the process!):

export {schoolName, firstSport, getPrincipal, fgPct};

export {schoolName as name, firstSport as sport ... };

You can also set a single member of the module to be the default export. This member is marked, unsurprisingly, with the default keyword, like so:

export default function () { ... };

export default 'foo';

You can mix both a default and named members, though that will complicate things when you later import these members.

With all these options, which should we choose? Choose the one that works! But if you are looking for expert guidance, Dave Herman, who is an integral part of the ES6 design process, suggested a preference for single export modules, using defaults. There was much discussion on this point, with many people in opposition, or at least expressing alternative preferences. It’s JavaScript: go with a style that works for your needs.

When using a module, we have two ways to interact with it: declarative and programmatic. The declarative syntax, shown in Listing 11-5, is simple and straightforward.

Listing 11-5. Using a Module Declaratively

import * as school from 'test-module';

console.log('The principal of %s is %s.', school.schoolName, school.getPrincipal());

The import keyword allows you to define which parts of the module are imported into what namespace. The module name is the same as the file name, minus the .js extension. Paths are allowed, so foo/bar would find bar.js under the directory foo relative to the current file location.

The import command is quite flexible, as you can import as much or as little of the module as you would like:

// Imports 'schoolName' only

import schoolName from 'test-module';

// Imports both of these named members

import {schoolName, getPrincipal} from 'test-module;

// Imports the default exports

import someDefault from 'test-module';

// Imports the default, plus a named member; DOES NOT WORK IN TRACEUR

import someDef {schoolName} from 'test-module';

// Imports the entire module, the default function/variable/whatever is

// available as default

import * as school from 'test-module';

console.log(s.default()); // Executes the function exported as a default

Modules are loaded asynchronously. Your browser will wait until all modules have loaded before executing any code. If you want greater control over this process (or prefer a different syntax) you can use the programmatic style of module import (Listing 11-6).

Listing 11-6. Programmatic Imports

System.import( 'test-module' )

.then( school => {

console.log('The principal of %s is %s.', school.schoolName, school.getPrincipal());

} )

.catch( error => {

console.error( 'Something has gone horribly wrong.' );

} );

The syntax of the underlying module does not change. Instead, we have a promise-based syntax for loading up modules if you want to tie specific code to the loading or execution of specific modules. (We threw in a use of the new arrow function syntax just for fun!) The details of what else you can do with this syntax are more complex than space here allows. Suffice it to say, you will have extensive control over the loading and running of your modules, should you want or need it.

Several polyfills for modules are available. Traceur does a reasonable job with modules, with one or two exceptions. The ES6-Tools page keeps up to date on a few others, including a package named, simply, ES6 Module Loader Polyfill ( https://github.com/ModuleLoader/es6-module-loader ). Note that ES6-Shim does not have a module polyfill.

Type Extensions

The last category of changes to ECMAScript is improvements to existing types. Some of these changes formalize features long available in JavaScript, like the HTML functions on String types and so on. Some of these are self-explanatory, but others could use some clarification, which is what we’re here to provide!

Strings

This set of String functions should be called from an instance of String. Put another way, they are available on String.prototype. First there are the HTML functions: anchor, big, bold, fixed, fontcolor, fontsize, italics, link, small, strike, sub, and sup. Each of these takes the Stringinstance and returns a copy wrapped in the appropriate tag. Various browsers may add or subtract from this group (Chrome, for instance, has a blink function).

There are also some String utility functions:

String.prototype.startsWith( str )

Does the String start with the provided substring?

String.prototype.endsWith( str )

Does the String end with the provided substring?

String.prototype.contains( str , [ startPos ])

Does the String contain the provided substring?

String.prototype.repeat( count )

Generates a new string, which is a repetition of String for count times.

Numbers

The Number type gains several static methods, most of which are used to determine characteristics of the passed argument.

Number.isNaN(num)

Is num a Number? Replaces the global isNaN function, which had some issues.

Number.isFinite(num)

Is num finite? Positive and negative Infinity, NaN, and non-numbers are not finite.

Number.isInteger(num)

Is the number an integer? NaN is not an integer, nor is anything non-numeric.

Number.isSafeInteger(num)

Can this number be safely represented as an IEEE-754 double-precision number, and does no other IEEE-754 double-precision number round to this number?

Number.parseInt( string , [ radix ])

Replaces global parseInt(); prefer supplying a radix, as this can ameliorate implementation differences.

Number.parseFloat( string )

Replaces global parseFloat().

Math

The Math utility library expands with a few useful functions. Most of these are more esoteric than we have time to go into here, but here is a brief list:

Math.imul(x,y)

Returns the result of the C-like 32-bit multiplication of the two parameters.

Math.clz32( num )

Returns the number of leading zero bits in the 32-bit binary representation of a number.

Math.fround( num )

Returns the nearest single-precision float representation of a number.

Math.log10( num )

Returns the base 10 logarithm of a number.

Math.log2( num )

Returns the base 2 logarithm of a number.

Math.log1p( num )

Returns the natural logarithm (base e) of 1 + a number.

Math.expm1( x )

Returns ex - 1, where x is the argument, and e the base of the natural logarithms.

Math.cosh( num )

Returns the hyperbolic cosine of a number.

Math.sinh( num )

Returns the hyperbolic sine of a number.

Math.tanh( num )

Returns the hyperbolic tangent of a number.

Math.acosh( num )

Returns the hyperbolic arc-cosine of a number.

Math.asinh( num )

Returns the hyperbolic arc-sine of a number.

Math.atanh( num )

Returns the hyperbolic arc-tangent of a number.

Math.hypot([num,num2,num3, ...])

Returns the square root of the sum of squares of its arguments.

Math.trunc( num )

Returns the integral part of a number by removing any fractional digits. It does not round any numbers.

Math.sign( num )

Returns the sign of a number, which may be positive, negative, or zero.

Math.cbrt( num )

Returns the cube root of a number.

Arrays

Many new things have happened in the world of Arrays. Let’s look at functions first. The first is a simple utility function: Array.from(), statically available on the Array type. This takes array-like objects, or iterable objects, and converts them to Arrays (giving you all the functions and features of Arrays). Think of arguments inside a function, or the return value of document.querySelectorAll, which are both like Arrays but lack many of the features of Arrays. Now you can convert iterables on-the-fly to Arrays.

Arrays also gain three new tools for iterating over their contents: keys, values, and entries. You can probably deduce that keys gives you the indices of an array, values the indices of the values, and entries an array consisting of the key and the value for each array entry. The part that’s a little different is that this is done via an iterator. That is, instead of getting all the values, you see the individual elements as you are passing over them. This can be useful for dynamic arrays, better memory management, array searches, and so on.

For searching through arrays, we already have the Array.prototype.indexOf function. But if you want to test for something other than simple equality, you can use Array.prototype.find and Array.prototype.findIndex. Both take an argument of a predicate function and an optional context in which to run. The predicate function, much like the predicate functions for forEach, map, and so on, takes arguments of element, index, and a reference back to the original code. Listing 11-7 shows an example.

Listing 11-7. Using Array.prototype.find()

var names = ['John', 'Jon', 'José', 'Joseph', 'Mike',

'Andre', 'Melanie', 'Jaymi', 'Kathy', 'Jennifer'];

names.find( function ( element, index ) {

if ( element.startsWith( 'J' ) ) {

console.log( 'The name %s at position %d starts with "J"!', element, index );

}

} );

Finally, there’s Array.prototype.fill, which allows you to fill an array with values, optionally passing in start and end positions.

There is one more important improvement to arrays: the spread operator. For a long time, JavaScript programmers have wanted to be able to “unwind” an array as an argument to a function. Up through ECMAScript 5, there was no way to do this, though you could useFunction.prototype.apply, which took an array and spread it out as arguments to the function being called. This was unwieldy and unclear to say the least. Now, you can use the spread operator:

[1, 2, 3].push(...[4, 5, 6])

[1, 2, 3, 4, 5, 6]

Nice, right? In many ways, this makes push, pop, shift, and unshift more powerful, and makes splice and concat much more specialized.

Polyfills

The Traceur transpiler does not support these expansions of standard types. Traceur, in general, is focused on new syntactical changes, not expansions of existing types.

On the other hand, ES6-shim supports all of these features. And you can find polyfills for most of these functions, often broken out by function or by type, at Addy Osmani’s ES6 Tools page.

New Collection Types

JavaScript has suffered from a dearth of collection implementations. Before ECMAScript Harmony, your only choices for native data strutures were the Array and the Object. This was hardly ideal. ECMAScript 6 introduces the Set, WeakSet, Map, and WeakMap as new data structures. At their most basic, these intend to be a new Collections API for JavaScript. In the future, you should be able to think of JavaScript collections as Map, WeakMap, Set, WeakSet, and Array, leaving Objects to do work as just objects, not double-duty as maps/associative arrays as well.

Let’s talk about the non-Weak versions of these objects first. The Map is a set of keys and values. The keys can be any primitive or object value, as can the values. These are explicitly intended to replace Objects as a data structure. There are some critical differences:

· Keys for Objects are always Strings, whereas keys for Maps can be any data type.

· Maps have a property for size, Objects do not; put another way, you have to track the size of your Objects manually, where Maps do this automatically.

· Objects have prototypes. Strictly speaking, Maps have prototypes as well, but Map instances do not have prototypes the way that Object instances do. This is important in that Objects have default keys, where Maps do not.

Sets are Arrays that guarantee uniqueness. Set entries must be unique according to === (triple-equals). Data can be retrieved from a Set in insertion order. A Set can be created from an Array by passing the Array as an argument to the Set constructor. It is not easy to convert a Set to an Array in cross-browser ECMAScript, but you could always iterate over the elements of a Set with for...of or Set.prototype.forEach and push the individual element onto an array, should you need to.

The new collections are supported by recent editions of both Firefox and Chrome. Internet Explorer has “basic” support for Map, WeakMap, and Set (though not WeakSet!). This means that IE knows about the type, and has some of it implemented, but does not, for instance, allow you to use new Map(iterable) to create an instance of Map. But you can create empty Maps, Sets, and WeakMaps and then add elements to them with the appropriate API calls. Full support is expected in the next edition of IE, according to status.modern.ie.

That’s Weak

So what’s the deal with the WeakSet and the WeakMap? To understand these, you have to understand a little bit about JavaScript garbage collection. In general, an object is available for garbage collection when its reference count is zero. That means there are no extant references to the object in question: no variables, no keys, no values, no entries and so on. The entries in Maps and Sets count as references.

Imagine that you have created a reference to a complex object and stored it in a variable. Later, you also stashed that reference in a Set. Before your function or section of code returned, you deallocated the variable, setting it equal to null. Great, right? All cleaned up! Not so fast. The entry in the Set persists and your object is not elligible for garbage collection until you remove it from the Set (or deallocate the entire Set!).

Enter WeakSets and WeakMaps. In the case of a WeakSet, the object references are held weakly. This means that the instance held in the Set does not count toward overall reference count. The reference is weakly held. If all other references to the object have been deallocated, the object is available for garbage collection. This can be quite useful for memory management but somewhat tricky if you were counting on that reference sticking around.

WeakMaps are similar in that their keys are held weakly, the same way as the entries for a Set are held weakly. Additionally, keys in a WeakMap can only be Objects, not primitves. Unlike regular Maps, the keys for WeakMaps cannot be iterated, because their state is nondeterministic, due to potential garbage collection. You will have to maintain a list of keys yourself, perhaps as an array, if you want access to it.

Collections API

The various collection implementations share a common API. Not all functions are found on all collections, but most are shared by two or more classes of collection.

Function/Property

Set

WeakSet

Map

WeakMap

Description

size

Y

Y

Y

N

Number of entries in the collection.

add(e)

Y

Y

N

N

Adds an element to the Set.

clear()

Y

Y

Y

Y

Removes all elements from the collection.

delete( k )

Y

Y

Y

Y

For Maps, deletes the entry for this key; for Sets, deletes the entry for this value.

has( k )

Y

Y

Y

Y

Returns Boolean true or false based on whether the key (Maps) or value (Sets) is present in the collection.

get( k )

N

N

Y

Y

Gets the value associated with this key.

set(k,v)

N

N

Y

Y

Sets the key k to value v.

entries()

Y

N

Y

N

Returns an iterator which will return individual arrays for key/ value pairs (Maps) or two-element arrays of each value (Sets).

forEach( fn , [ scope ])

Y

N

Y

N

Iterates over the elements of the collection, running the function fn once for each element (values for Sets, key/value pairs as an array for Maps).

keys()

Y

N

Y

N

Returns the keys for the collection. In Sets, keys and values are the same thing.

values()

Y

N

Y

N

Returns the values for the collection.

Polyfills

As with the extensions to JavaScript types, Traceur does not implement ES5 transpilations of these features. That is the realm of the polyfill. And, as noted earlier, the ES6-Shim polyfill has ES5-compliant implementations of Map, Set, WeakMap, and WeakSet. There’s also the harmony-collections polyfill ( https://github.com/Benvie/harmony-collections ), which implements only Map, Set, WeakMap, and WeakSet.

Keep in mind, though, that while a polyfill can ape the API, it cannot duplicate a critical feature of WeakMaps and WeakSets: holding references weakly so that they don’t count toward overall reference count for garbage collection purposes. That can only be implemented through changes to the JavaScript engine. It can only emulate the behavior of a WeakMap, without having an actual relationship with the garbage collector whereby weakly held references can be released.

Summary

In this chapter, we have tried to give you an overview of some of the new features coming to JavaScript with ECMAScript 6. In doing so, we have necessarily overlooked a large amount of the specification. Some time in 2015, the specification will settle into its final form, and APress will offer several books covering the material.

Meanwhile, we have focused on the tools that ameliorate some of JavaScript’s more annoying quirks (think arrow functions here). We also walked through how to use some features that many programmers are using today (promises, classes, modules). We are very excited about the future of JavaScript. With ECMAScript 6 well defined by the time this book is published, we are looking forward to ECMAScript 7, which TC39 is already starting to work on!