ES2019 features coming to JavaScript (starring us!)

Shape Security has been contributing actively to TC39 and other standards bodies for the past 4 years but this year is special for us. A significant portion of the features coming to JavaScript as part of the 2019 update are from Shape Security engineers! Shape contributes to standards bodies to ensure new features are added while taking into account evolving security implications. Anything Shape contributes outside of this broad goal is because we believe the web platform is the greatest platform ever made and we want to help it grow even better.

Thanks to everyone who contributes to TC39 and thank you Michael Ficarra (@smooshMap), Tim Disney (@disnet), and Kevin Gibbons (@bakkoting) for representing Shape.

TL;DR

The 2019 update includes quality-of-life updates to JavaScript natives, and standardizes undefined or inconsistent behavior.

Buffed: String, Array, Object, Symbol, JSON

Nerfed: Try/Catch

Adjusted: Array.prototype.sort, Function.prototype.toString

Native API additions

Array.prototype.flat & .flatMap

> [ [1], [1, 2], [1, [2, [3] ] ] ].flat();
< [1, 1, 2, 1, [2, [3]]]

> [ [1], [1, 2], [1, [2, [3] ] ] ].flat(3);
< [1, 1, 2, 1, 2, 3]

The Array prototype methods flat and flatMap got unexpected attention this year, not because of their implementation, but because Shape Security engineer Michael Ficarra opened a gag pull request renaming the original method flatten to smoosh thus starting SmooshGate. Michael opened the pull request as a joke after long TC39 meetings on the topic and it ended up giving the average developer great insight into how TC39 works and under how big of a microscope proposals are placed under. When considering new features to add to JavaScript, the TC39 committee has to take two decades of existing websites and applications into account to ensure no new feature unexpectedly breaks them.

After FireFox shipped flatten in the nightly releases, users found that websites using the MooTools framework were breaking. MooTools had added flatten to the Array prototype ten years ago and now any site using MooTools risks breaking if the method changes. Since MooTools usage has declined in favor of more modern frameworks, many sites using the library are sites which are no longer actively maintained — they will not be updated even if MooTools released an updated version. SmooshGate ended up surfacing serious discussions as to what degree existing websites affect future and present innovation.

The committee concluded backwards compatibility was of higher importance and renamed the method flatten to flat. It’s a long, complicated story with an anticlimactic ending but that could be said of all specification work.

Drama aside, flat operates on an array and “flattens” nested arrays within to a configurable depth. flatMap operates similarly to the map method by applying a function to each element in the list and then calling flat() on the resulting list.

Object.fromEntries

let obj = { a: 1, b: 2 };
let entries = Object.entries(obj);
let newObj = Object.fromEntries(entries);

Object.fromEntries is a complement to the Object.entries method which allows a developer to more succinctly translate objects from one another. Object.entries takes a regular JavaScript object and returns a list of [key, value] pairs, Object.fromEntries enables the reverse.

String.prototype.trimStart & .trimEnd

> '   hello world   '.trimStart()
< "hello world   "

> '   hello world   '.trimEnd()
< "   hello world"

Major JavaScript engines had implementations of String.prototype.trimLeft() and String.prototype.trimRight() but the methods lacked a true definition in the spec. This proposal standardizes the names as trimStart and trimEnd, aligning terminology with padStart and padEnd, and aliases trimLeft and trimRight to the respective function.

Symbol.prototype.description

> let mySymbol = Symbol('my description');
< undefined

> mySymbol.description
< 'my description'

Symbol.prototype.description is an accessor for the unexposed description property. Before this addition, the only way to access the description passed into the constructor was by converting the Symbol to a string via toString() and there was no intuitive way to differentiate between Symbol() and Symbol(‘’).

Spec & Language Cleanup

Try/Catch optional binding

try {
  throw new Error();
} catch {
  console.log('I have no error')
}

Until this proposal, omitting the binding on catch resulted in an error when parsing the JavaScript source text. This resulted in developers putting in a dummy binding despite them being unnecessary and unused. This is another quality-of-life addition allowing developers to be more intentional when they ignore errors, improving the developer experience and reducing cognitive overhead for future maintainers.

Make ECMAScript a proper superset of JSON

JSON.parse describes JSON as a subset of JavaScript despite valid JSON including Unicode line separators and paragraph separators not being valid JavaScript. This proposal modifies the ECMAScript specification to allow those characters in string literals. The majority of developers will never encounter this usage but it reduces edge case handling for developers dealing with the go-between and generation of JavaScript and JSON. Now you can insert any valid JSON into a JavaScript program without accounting for edge cases in a preprocessing stage.

Well-formed JSON.stringify

> JSON.stringify('\uD834\uDF06')
< "\"𝌆\""

> JSON.stringify('\uDF06\uD834')
< "\"\\udf06\\ud834\""

This proposal rectifies inconsistencies in description and behavior for JSON.stringify. The ECMAScript spec describes JSON.stringify as returning a UTF-16 encoded JSON format string but can return values that are invalid UTF-16 and are unrepresentable in UTF-8 (specifically surrogates in the Unicode range U+D800U+DFFF). The accepted resolution is, when encoding lone surrogates, to return the code point as a Unicode escape sequence.

Stable Array.prototype.sort()

This is a change to the spec reflecting the behavior standardized by practice in major JavaScript engines. Array.prototype.sort is now required to be stable — values comparing as equal stay in their original order.

Revised Function.prototype.toString()

The proposal to revise Function.prototype.toString has been a work-in-progress for over 3 years and was another proposed and championed by Michael Ficarra due to problems and inconsistencies with the existing spec. This revision clarifies and standardizes what source text toString() should return or generate for functions defined in all the different forms. For functions created from parsed ECMAScript source, toString() will preserve the whole source text including whitespace, comments, everything.

Onward and upward

ES2015 was a big step for JavaScript with massive new changes and, because of the problems associated with a large change set, the TC39 members agreed it is more sustainable to produce smaller, yearly updates. Most of the features above are already implemented in major JavaScript engines and can be used today.

If you are interested in reading more TC39 proposals, including dozens which are in early stages, the committee makes its work available publicly on Github.com. Take a look at some of the more interesting proposals like the pipeline operator and optional chaining.