2013-03-21

ECMAScript 6: TC39’s January 2013 meeting

TC39 [1] is the committe that currently plans ECMAScript 6 (code-named ECMAScript.next), the next version of the JavaScript language standard. January 29–31, they had another meeting. Thanks to Rick Waldron’s notes, we can read up on what has been decided. This blog post describes the highlights. Previous blog posts summarized prior meetings.

(Note: a blog post on the March TC39 meeting will be posted at some time in the future.)

January 29

  • Object.is(): strict equality (===) is safer than sloppy equality (==). But it still has the quirk of NaN not being equal to itself [2]. The original plan for ECMAScript 6 was to introduce an is operator that fixes this quirk, but those plans have been abandoned. Instead, you can use Object.is(op1, op2) if you need “stricter” equality [2]. Object.is() also distinguishes positive and negative zero [3] (which isn’t always desirable).
  • ECMAScript Internationalization API (ECMA-402): Chrome 24 shipped with an (unprefixed) implementation of the first edition of ECMA-402. Thanks to the API, the methods String.prototype.localeCompare, {Date,Number}.prototype.toLocaleString get meaningful implementations (until now, they didn’t do anything locale-specific on most JavaScript engines). Work to specify the second edition of ECMA-402 continues.

January 30

Static methods

ECMAScript 6 will allow one to define static methods (constructor methods) in classes. For example:
    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        static zero() {
            return new Point(0, 0);
        }
    }
This is the same as:
    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
    }
    Object.mixin(Point, {
        zero() {
            return new Point(0, 0);
        }
    });

An Array.prototype.map() that doesn’t return an array

Arrays have several methods that create a new array based on the current one. For example, concat and filter. If those methods are invoked on a subtype of Array, it is obvious what type the result should have: the same type as this. For map, things are more complicated: that method is translating from one kind of value to another one and you sometimes might want to change the collection that the result is in, too. Hence, subtypes of Array will inherit the following method (Array-like types might get it, too):
    Array.from(iterable, mapFunction?, thisValue?)
Compared to a previous version [4], the second and third parameter are new. The receiver (this) of from() determines which subtype of Array the result will be stored in. Example from the Waldron’s notes:
    // Turn an array of nodeNames into NodeList of nodes
    NodeList.from( ["div"], node => document.createElement(node) );

Destructuring is refutable by default

Destructuring is used when a value is ready to be stored somewhere: it allows you to extract data from that value via patterns on the receiving end (left-hand side of an assignment, parameter definitions, etc.). Refutable matching means that the extraction can fail and throw an exception. It has been decided that refutability should be the default for destructuring in ECMAScript 6 (“fail-fast” operation). That is, destructuring only succeeds if the value to be stored completely matches the pattern:
    { first: f, last: l } = { first: 'Jane', last: 'Doe' };  // succeeds
    { first: f, last: l } = { first: 'Jane' };  // fails
You can, however introduce irrefutability by suffixing parts of the pattern with a question mark (rule of thumb: the question mark follows pattern parts, not variables):
    { first: f, last?: l } = { first: 'Jane' };  // succeeds
    console.log(l);  // undefined
Another possibility is to give a left-hand-side variable a default value:
    { first: f, last: l = 'Unknown' } = { first: 'Jane' };  // succeeds
    console.log(l);  // Unknown
A default value is triggered if the part of the pattern is missing or if its value is undefined:
    let { a: x = 1 } = { a: undefined }
    console.log(x);  // 1
You can also make all of a pattern irrefutable:
    let { foo: f }? = options;  // always succeeds
f becomes undefined if options is undefined or null or does not have a property foo. This example shows that irrefutability is “deep”: if you mark a part as irrefutable, the parts inside of it become irrefutable, too. A proposal on the ECMAScript wiki has details on refutable matching, but uses a slightly different syntax (prefix question marks).

Property name of functions

ECMAScript 6 will standardize the property name of functions. Its main use is debugging: you get a name if you log a function and debuggers can use the name, too. ECMAScript 6 will try to fill in the name as best it can: In addition to the obvious cases (function declaration, named function expression, method definition), a function will also get a name if it is the initial value of a variable declaration (var, let, const) or a property value inside an object literal.

TypedArray becomes part of ECMAScript

TypedArray is a JavaScript type that was created for WebGL to handle binary data. TC39 will move this standard into ECMAScript 6. In the process, it can become a more organic part of the language (e.g. become more similar to arrays).

January 31

Modules are still work in progress. The following subsections describe other decisions.

Refining the semantics of classes

Classes can’t extend non-constructors. If you write a class:
    class Foo extends X { ... }
Then X must be a constructor (X will become Foo’s prototype and X.prototype will become Foo.prototype’s prototype). You can, however extend null:
    class Bar extends null { ... }
Roughly, this is syntactic sugar for:
    function Bar() { ... }
    Bar.prototype = Object.create(null);
    ...

Map/Set comparator

You will be able to configure how these ECMAScript 6 data structures compare keys or values in order to implement the operations get, set, has, delete. The new signatures are (the second parameter is new):
    new Map(iterator = undefined, comparator = undefined)
    new Set(iterator = undefined, comparator = undefined)
If you don’t specify a comparator, a “default comparator” is used that works like === [6], but considers NaN to be equal to itself. Otherwise you wouldn’t be able do anything with NaN, apart from adding it to a collection. Alternatively, you can also specify the comparator to be 'is', which means that Object.is() will be used [2]. That function works like the default comparator, but distinguishes +0 and -0 [3].

Simplified syntax for array comprehensions and generator comprehensions

Previously, array comprehensions [7] looked like this:
    let arr = [x for x of itr if x > 0];
This has been changed to:
    let arr = [for (x of itr) if (x > 0) x];
Now the element expression comes last, which is more similar to the structure of an implementation that uses loops. The expression is preceded by zero or more comprehension clauses. Those clauses now have parenthesized syntax. There previously was a let clause, which may or may not be added after ECMAScript 6. Arguably, let would make comprehensions more complex, while their main appeal is simplicity. Details of the new syntax are described in a gist by David Herman.

References

  1. ECMAScript: ES.next versus ES 6 versus ES Harmony
  2. Stricter equality in JavaScript
  3. JavaScript’s two zeros
  4. ECMAScript.next: Array.from() and Array.of()
  5. ECMAScript 6’s parameter destructuring and forEach()
  6. Equality in JavaScript: === versus ==
  7. ECMAScript.next: array comprehensions and generator comprehensions

No comments: