ES6 In Depth: The Future

ES6 In Depth is a series on new features being added to the JavaScript programming language in the 6th Edition of the ECMAScript standard, ES6 for short.

Last week’s article on ES6 modules wrapped up a 4-month survey of the major new features in ES6.

This post covers over a dozen more new features that we never got around to talking about at length. Consider it a fun tour of all the closets and oddly-shaped upstairs rooms in this mansion of a language. Maybe a vast underground cavern or two. If you haven’t read the other parts of the series, take a look; this installment may not be the best place to start!

(a picture of the Batcave, inexplicably)
“On your left, you can see typed arrays…”

One more quick warning: Many of the features below are not widely implemented yet.

OK. Let’s get started.

Features you may already be using

ES6 standardizes some features that were previously in other standards, or widely implemented but nonstandard.

  • Typed arrays, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer" target="_blank">ArrayBuffer</a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView" target="_blank">DataView</a>. These were all standardized as part of WebGL, but they’ve been used in many other APIs since then, including Canvas, the Web Audio API, and WebRTC. They’re handy whenever you need to process large volumes of raw binary or numeric data.

    For example, if the Canvas rendering context is missing a feature you want, and if you’re feeling sufficiently hardcore about it, you can just implement it yourself:

    <pre>
    var context = canvas.getContext("2d");
    var image = context.getImageData(0, 0, canvas.width, canvas.height);
    var pixels = image.data; // a Uint8ClampedArray object
    // ... Your code here!
    // ... Hack on the raw bits in `pixels`
    // ... and then write them back to the canvas:
    context.putImageData(image, 0, 0);
    </pre>

    During standardization, typed arrays picked up methods like .slice(), .map(), and .filter().

  • Promises. Writing just one paragraph about promises is like eating just one potato chip. Never mind how hard it is; it barely even makes sense as a thing to do. What to say? Promises are the building blocks of asynchronous JS programming. They represent values that will become available later. So for example, when you call <a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch" target="_blank">fetch()</a>, instead of blocking, it returns a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank">Promise</a> object immediately. The fetch goes on in the background, and it’ll call you back when the response arrives. Promises are better than callbacks alone, because they chain really nicely, they’re first-class values with interesting operations on them, and you can get error handling right with a lot less boilerplate. They’re polyfillable in the browser. If you don’t already know all about promises, check out Jake Archibald’s very in-depth article.

  • Functions in block scope. You shouldn’t be using this one, but it’s possible you have been. Maybe unintentionally.

    In ES1-5, this code was technically illegal:

    <pre>
    if (temperature > 100) {
    function chill() {
    return fan.switchOn().then(obtainLemonade);
    }
    chill();
    }
    </pre>

    That function declaration inside an if block was supposedly forbidden. They were only legal at toplevel, or inside the outermost block of a function.

    But it worked in all major browsers anyway. Sort of.

    Not compatibly. The details were a little different in each browser. But it sort of worked, and many web pages still use it.

    ES6 standardizes this, thank goodness. The function is hoisted to the top of the enclosing block.

    Unfortunately, Firefox and Safari don’t implement the new standard yet. So for now, use a function expression instead:

    <pre>
    if (temperature > 100) {
    var chill = function () {
    return fan.switchOn().then(obtainLemonade);
    };
    chill();
    }
    </pre>

    The only reason block-scoped functions weren’t standardized years ago is that the backward-compatibility constraints were incredibly complicated. Nobody thought they could be solved. ES6 threads the needle by adding a very strange rule that only applies in non-strict code. I can’t explain it here. Trust me, use strict mode.

  • Function names. All the major JS engines have also long supported a nonstandard .name property on functions that have names. ES6 standardizes this, and makes it better by inferring a sensible .name for some functions that were heretofore considered nameless:

    <pre>
    > var lessThan = function (a, b) { return a < b; };
    > lessThan.name
    "lessThan"
    </pre>

    For other functions, such as callbacks that appear as arguments to .then methods, the spec still can’t figure out a name. <var>fn</var>.name is then the empty string.

Nice things

  • <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign" target="_blank">Object.assign(target, ...sources)</a>. A new standard library function, similar to Underscore’s <a href="http://underscorejs.org/#extend" target="_blank">_.extend()</a>.

  • The spread operator for function calls. This is nothing to do with Nutella, even though Nutella is a tasty spread. But it is a delicious feature, and I think you’ll like it.

    Back in May, we introduced rest parameters. They’re a way for functions to receive any number of arguments, a more civilized alternative to the random, clumsy arguments object.

    <pre>
    function log(...stuff) { // stuff is the rest parameter.
    var rendered = stuff.map(renderStuff); // It's a real array.
    $("#log").add($(rendered));
    }
    </pre>

    What we didn’t say is that there’s matching syntax for passing any number of arguments to a function, a more civilized alternative to fn.apply():

    <pre>
    // log all the values from an array
    log(...myArray);
    </pre>

    Of course it works with any iterable object, so you can log all the stuff in a Set by writing log(...mySet).

    Unlike rest parameters, it makes sense to use the spread operator multiple times in a single argument list:

    <pre>
    // kicks are before trids
    log("Kicks:", ...kicks, "Trids:", ...trids);
    </pre>

    The spread operator is handy for flattening an array of arrays:

    <pre>
    > var smallArrays = [[], ["one"], ["two", "twos"]];
    > var oneBigArray = [].concat(...smallArrays);
    > oneBigArray
    ["one", "two", "twos"]
    </pre>

    …but maybe this one of those pressing needs that only I have. If so, I blame Haskell.

  • The spread operator for building arrays. Also back in May, we talked about “rest” patterns in destructuring. They’re a way to get any number of elements out of an array:

    <pre>
    > var [head, ...tail] = [1, 2, 3, 4];
    > head
    1
    > tail
    [2, 3, 4]
    </pre>

    Guess what! There’s matching syntax for getting any number of elements into an array:

    <pre>
    > var reunited = [head, ...tail];
    > reunited
    [1, 2, 3, 4]
    </pre>

    This follows all the same rules as the spread operator for function calls: you can use the spread operator many times in the same array, and so on.

  • Proper tail calls. This one is too amazing for me to try to explain here.

    To understand this feature, there’s no better place to start than page 1 of Structure and Interpretation of Computer Programs. If you enjoy it, just keep reading. Tail calls are explained in section 1.2.1, “Linear Recursion and Iteration”. The ES6 standard requires that implementations be “tail-recursive”, as the term is defined there.

    None of the major JS engines have implemented this yet. It’s hard to implement. But all in good time.

Text

  • Unicode version upgrade. ES5 required implementations to support at least all the characters in Unicode version 3.0. ES6 implementations must support at least Unicode 5.1.0. You can now use characters from Linear B in your function names!

    Linear A is still a bit risky, both because it was not added to Unicode until version 7.0 and because it might be hard to maintain code written in a language that has never been deciphered.

    (Even in JavaScript engines that support the emoji added in Unicode 6.1, you can’t use 😺 as a variable name. For some reason, the Unicode Consortium decided not to classify it as an identifier character. 😾)

  • Long Unicode escape sequences. ES6, like earlier versions, supports four-digit Unicode escape sequences. They look like this: \u212A. These are great. You can use them in strings. Or if you’re feeling playful and your project has no code review policy whatsoever, you can use them in variable names. But then, for a character like U+13021 (𓀡), the Egyptian hieroglyph of a guy standing on his head, there’s a slight problem. The number 13021 has five digits. Five is more than four.

    In ES5, you had to write two escapes, a UTF-16 surrogate pair. This felt exactly like living in the Dark Ages: cold, miserable, barbaric. ES6, like the dawn of the Italian Renaissance, brings tremendous change: you can now write \u{13021}.

  • Better support for characters outside the BMP. The .toUpperCase() and .toLowerCase() methods now work on strings written in the Deseret alphabet!

    In the same vein, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint" target="_blank">String.fromCodePoint(...codePoints)</a> is a function very similar to the older <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode" target="_blank">String.fromCharCode(...codeUnits)</a>, but with support for code points beyond the BMP.

  • Unicode RegExps. ES6 regular expressions support a new flag, the u flag, which causes the regular expression to treat characters outside the BMP as single characters, not as two separate code units. For example, without the u, /./ only matches half of the character "😭". But /./<strong>u</strong> matches the whole thing.

    Putting the u flag on a RegExp also enables more Unicode-aware case-insensitive matching and long Unicode escape sequences. For the whole story, see Mathias Bynens’s very detailed post.

  • Sticky RegExps. A non-Unicode-related feature is the y flag, also known as the sticky flag. A sticky regular expression only looks for matches starting at the exact offset given by its .lastIndex property. If there isn’t a match there, rather than scanning forward in the string to find a match somewhere else, a sticky regexp immediately returns null.

  • An official internationalization spec. ES6 implementations that provide any internationalization features must support ECMA-402, the ECMAScript 2015 Internationalization API Specification. This separate standard specifies the Intl object. Firefox, Chrome, and IE11+ already fully support it. So does Node 0.12.

Numbers

  • Binary and octal number literals. If you need a fancy way to write the number 8,675,309, and 0x845fed isn’t doing it for you, you can now write 0o41057755 (octal) or 0b100001000101111111101101 (binary).

    Number(str) also now recognizes strings in this format: Number("0b101010") returns 42.

    (Quick reminder: <var>number</var>.toString(base) and parseInt(<var>string</var>, base) are the original ways to convert numbers to and from arbitrary bases.)

  • New Number functions and constants. These are pretty niche. If you’re interested, you can browse the standard yourself, starting at <a href="http://www.ecma-international.org/ecma-262/6.0/index.html#sec-number.epsilon" target="_blank">Number.EPSILON</a>.

    Maybe the most interesting new idea here is the “safe integer” range, from −(253 – 1) to +(253 – 1) inclusive. This special range of numbers has existed as long as JS. Every integer in this range can be represented exactly as a JS number, as can its nearest neighbors. In short, it’s the range where ++ and -- work as expected. Outside this range, odd integers aren’t representable as 64-bit floating-point numbers, so incrementing and decrementing the numbers that are representable (all of which are even) can’t give a correct result. In case this matters to your code, the standard now offers constants <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER" target="_blank">Number.MIN_SAFE_INTEGER</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER" target="_blank">Number.MAX_SAFE_INTEGER</a>, and a predicate <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger" target="_blank">Number.isSafeInteger(n)</a>.

  • New Math functions. ES6 adds hyperbolic trig functions and their inverses, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt" target="_blank">Math.cbrt(x)</a> for computing cube roots, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot" target="_blank">Math.hypot(x, y)</a> for computing the hypotenuse of a right triangle, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2" target="_blank">Math.log2(x)</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10" target="_blank">Math.log10(x)</a> for computing logarithms in common bases, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32" target="_blank">Math.clz32(x)</a> to help compute integer logarithms, and a few others.

    <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign" target="_blank">Math.sign(x)</a> gets the sign of a number.

    ES6 also adds <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul" target="_blank">Math.imul(x, y)</a>, which does signed multiplication modulo 232. This is a very strange thing to want… unless you are working around the fact that JS does not have 64-bit integers or big integers. In that case it’s very handy. This helps compilers. Emscripten uses this function to implement 64-bit integer multiplication in JS.

    Similarly <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround" target="_blank">Math.fround(x)</a> is handy for compilers that need to support 32-bit floating-point numbers.

The end

Is this everything?

Well, no. I didn’t even mention the object that’s the common prototype of all built-in iterators, the top-secret GeneratorFunction constructor, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is" target="_blank">Object.is(v1, v2)</a>, how Symbol.species helps support subclassing builtins like Array and Promise, or how ES6 specifies details of how multiple globals work that have never been standardized before.

I’m sure I missed a few things, too.

But if you’ve been following along, you have a pretty good picture of where we’re going. You know you can use ES6 features today, and if you do, you’ll be opting in to a better language.

A few days ago, Josh Mock remarked to me that he had just used eight different ES6 features in about 50 lines of code, without even really thinking about it. Modules, classes, argument defaults, Set, Map, template strings, arrow functions, and let. (He missed the forof loop.)

This has been my experience, too. The new features hang together very well. They end up affecting almost every line of JS code you write.

Meanwhile, every JS engine is hurrying to implement and optimize the features we’ve been discussing for the past few months.

Once we’re done, the language will be complete. We’ll never have to change anything again. I’ll have to find something else to work on.

Just kidding. Proposals for ES7 are already picking up steam. Just to pick a few:

  • Exponentation operator. 2 ** 8 will return 256. Implemented in Firefox Nightly.

  • <a href="https://github.com/tc39/Array.prototype.includes/" target="_blank">Array.prototype.includes(value)</a>. Returns true if this array contains the given value. Implemented in Firefox Nightly; polyfillable.

  • SIMD. Exposes 128-bit SIMD instructions provided by modern CPUs. These instructions do an arithmetic operation on 2, or 4, or 8 adjacent array elements at a time. They can dramatically speed up a wide variety of algorithms for streaming audio and video, cryptography, games, image processing, and more. Very low-level, very powerful. Implemented in Firefox Nightly; polyfillable.

  • Async functions. We hinted at this feature in the post on generators. Async functions are like generators, but specialized for asynchronous programming. When you call a generator, it returns an iterator. When you call an async function, it returns a promise. Generators use the yield keyword to pause and produce a value; async functions instead use the await keyword to pause and wait for a promise.

    It’s hard to describe them in a few sentences, but async functions will be the landmark feature in ES7.

  • Typed Objects. This is a follow-up to typed arrays. Typed arrays have elements that are typed. A typed object is simply an object whose properties are typed.

    <pre>
    // Create a new struct type. Every Point has two fields
    // named x and y.
    var Point = new TypedObject.StructType({
    x: TypedObject.int32,
    y: TypedObject.int32
    });

    // Now create an instance of that type.
    var p = new Point({x: 800, y: 600});
    console.log(p.x); // 800
    </pre>

    You would only do this for performance reasons. Like typed arrays, typed objects offer a few of the benefits of typing (compact memory usage and speed), but on a per-object, opt-in basis, in contrast to languages where everything is statically typed.

    They’re are also interesting for JS as a compilation target.

    Implemented in Firefox Nightly.

  • Class and property decorators. Decorators are tags you add to a property, class, or method. An example shows what this is about:

    <pre>
    import debug from "jsdebug";

    class Person {
    @debug.logWhenCalled
    hasRoundHead(assert) {
    return this.head instanceof Spheroid;
    }
    ...
    }
    </pre>

    @debug.logWhenCalled is the decorator here. You can imagine what it does to the method.

    The proposal explains how this would work in detail, with many examples.

There’s one more exciting development I have to mention. This one is not a language feature.

TC39, the ECMAScript standard committee, is moving toward more frequent releases and a more public process. Six years passed between ES5 and ES6. The committee aims to ship ES7 just 12 months after ES6. Subsequent editions of the standard will be released on a 12-month cadence. Some of the features listed above will be ready in time. They will “catch the train” and become part of ES7. Those that aren’t finished in that timeframe can catch the next train.

It’s been great fun sharing the staggering amount of good stuff in ES6. It’s also a pleasure to be able to say that a feature dump of this size will probably never happen again.

Thanks for joining us for ES6 In Depth! I hope you enjoyed it. Keep in touch.

About Jason Orendorff

More articles by Jason Orendorff…


6 comments

  1. Johannes Brodwall

    Thanks for a great series of articles. ES6 has been an exciting time to be a developer.

    August 22nd, 2015 at 07:12

  2. Igor

    Thank you! It has been a great series to discover and learn. Excited for all the new features coming soon!

    August 22nd, 2015 at 16:45

  3. Luke

    The function.name is an interesting one – I wonder how this might be used and how it would work with minifiers?

    Is it generally used just for debugging work?

    August 23rd, 2015 at 21:46

  4. voracity

    Thanks for all the articles, they’ve been fantastic — very well written and insightful. I won’t complain if you (and others) decide to write some more ***-in-depths.

    I wasn’t aware that decorators were a possibility for ES7. That makes me happy. Inside (procedurally defined) modules and for functions/vars would also be great. (Sure, the latter is not as useful, but it can make some things cleaner — e.g. a dynamic commenting/documentation system.)

    “…async functions will be the landmark feature in ES7.” :) Very excited. This may be the biggest change (in practice) in all of JavaScript’s history.

    August 23rd, 2015 at 23:45

  5. Phil Dokas

    Thanks for this series, it’s been a very valuable and approachable lesson in lots of the good stuff. I hope you do it again for ES7 and beyond!

    August 29th, 2015 at 16:39

  6. Rahul garg

    Thank you. I learned a lot about es6 features from your series, still is learning and have long way to go. They will be really helpful.

    September 7th, 2015 at 10:58

Comments are closed for this article.