After the release of ECMAScript 2015, a.k.a. ES6, the ECMAScript Language Specification is evolving rapidly: it’s getting many new features that will help developing web applications, with a new release planned every year.
Last week, Firefox Nightly 54 reached 100% on the Kangax ECMAScript 2016+ compatibility table that currently covers ECMAScript 2016 and the ECMAScript 2017 draft.
Here are some highlights for those spec updates.
ECMAScript 2016
ECMAScript 2016 is the latest stable edition of the ECMAScript specification. ECMAScript 2016 introduces two new features, the Exponentiation Operator and Array.prototype.includes
, and also contains various minor changes.
New Features
Exponentiation Operator
Status: Available from Firefox 52 (now Beta, will ship in March 2017).
The exponentiation operator (**
) allows infix notation of exponentiation.
It’s a shorter and simpler replacement for Math.pow
. The operator is right-associative.
// without Exponentiation Operator console.log(Math.pow(2, 3)); // 8 console.log(Math.pow(2, Math.pow(3, 2)); // 512 // with Exponentiation Operator console.log(2 ** 3); // 8 console.log(2 ** 3 ** 2); // 512
- Specification
- Documentation
- Bugzilla Links
Array.prototype.includes
Status: Available from Firefox 43.
Array.prototype.includes
is an intuitive way to check the existence of an element in an array, replacing the array.indexOf(element) !== -1
idiom.
let supportedTypes = [ "text/plain", "text/html", "text/javascript", ]; // without Array.prototype.includes. console.log(supportedTypes.indexOf("text/html") !== -1); // true console.log(supportedTypes.indexOf("image/png") !== -1); // false // with Array.prototype.includes. console.log(supportedTypes.includes("text/html")); // true console.log(supportedTypes.includes("image/png")); // false
- Specification
- Documentation
- Bugzilla Links
Miscellaneous Changes
Generators can’t be constructed
Status: Available from Firefox 43.
Calling generator with new now throws.
function* g() { } new g(); // throws
- Specifications
- Bugzilla Link
Iterator for yield* can catch throw()
Status: Available from Firefox 27.
When Generator.prototype.throw
is called on a generator while it’s executing yield*
, the operand of the yield*
can catch the exception and return to normal completion.
function* inner() { try { yield 10; yield 11; } catch (e) { yield 20; } } function* outer() { yield* inner(); } let g = outer(); console.log(g.next().value); // 10 console.log(g.throw().value); // 20, instead of throwing
- Specifications
- Documentation
- Bugzilla Link
Function with non-simple parameters can’t have “use strict”
Status: Available from Firefox 52 (now Beta, will ship in March 2017).
When a function has non-simple parameters (destructuring parameters, default parameters, or rest parameters), the function can’t have the "use strict"
directive in its body.
function assertEq(a, b, message="") { "use strict"; // error // ... }
However, functions with non-simple parameters can appear in code that’s already strict mode.
"use strict"; function assertEq(a, b, message="") { // ... }
- Specification
- Bugzilla Link
Nested RestElement in Destructuring
Status: Available from Firefox 47.
Now a rest pattern in destructuring can be an arbitrary pattern, and also be nested.
let [a, ...[b, ...c]] = [1, 2, 3, 4, 5]; console.log(a); // 1 console.log(b); // 2 console.log(c); // [3, 4, 5] let [x, y, ...{length}] = "abcdef"; console.log(x); // "a" console.log(y); // "b" console.log(length); // 4
- Specification
- Bugzilla Link
Remove [[Enumerate]]
Status: Removed in Firefox 47.
The enumerate
trap of the Proxy handler has been removed.
- Specification
- Documentation
- Bugzilla Link
ECMAScript 2017 draft
ECMAScript 2017 will be the next edition of ECMAScript specification, currently in draft. The ECMAScript 2017 draft introduces several new features, Object.values
/ Object.entries
, Object.getOwnPropertyDescriptors
, String padding, trailing commas in function parameter lists and calls, Async Functions, Shared memory and atomics, and also some minor changes.
New Features
Async Functions
Status: Available from Firefox 52 (now Beta, will ship in March 2017).
Async functions help with long promise chains broken up to separate scopes, letting you write the chains just like a synchronous function.
When an async function is called, it returns a Promise
that gets resolved when the async function returns. When the async function throws, the promise gets rejected with the thrown value.
An async function can contain an await
expression. That receives a promise and returns a resolved value. If the promise gets rejected, it throws the reject reason.
async function makeDinner(candidates) { try { let itemsInFridge = await fridge.peek(); let itemsInStore = await store.peek(); let recipe = await chooseDinner( candidates, itemsInFridge, itemsInStore, ); let [availableItems, missingItems] = await fridge.get(recipe.ingredients); let boughtItems = await store.buy(missingItems); pot.put(availableItems, boughtItems); await pot.startBoiling(recipe.temperature); do { await timer(5 * 60); } while(taste(pot).isNotGood()); await pot.stopBoiling(); return pot; } catch (e) { return document.cookie; } } async function eatDinner() { eat(await makeDinner(["Curry", "Fried Rice", "Pizza"])); }
- Specification Drafts
- Documentation
- Bugzilla Link
Shared memory and atomics
Status: Available behind a flag from Firefox 52 (now Beta, will ship in March 2017).
SharedArrayBuffer
is an array buffer pointing to data that can be shared between Web Workers. Views on a shared memory can be created with the TypedArray
and DataView
constructors.
When transferring SharedArrayBuffer
between the main thread and a worker, the underlying data is not transferred but instead the information about the data in memory is sent. As a result it reduces the cost of using a worker to process data retrieved on main thread, and also makes it possible to process data in parallel on multiple workers without creating separate data for each.
// main.js let worker = new Worker("worker.js"); let sab = new SharedArrayBuffer(IMAGE_SIZE); worker.onmessage = functions(event) { let image = createImageFromBitmap(event.data.buffer); document.body.appendChild(image); }; captureImage(sab); worker.postMessage({buffer: sab}) // worker.js onmessage = function(event) { let sab = event.data.buffer; processImage(sab); postMessage({buffer: sab}); };
Moreover, a new API called Atomics
provides low-level atomic access and synchronization primitives for use with shared memory. Lars T Hansen has already written about this in the Mozilla Hacks post “A Taste of JavaScript’s New Parallel Primitives“.
- Specifications Draft
- Documentation
- Bugzilla Links
Object.values / Object.entries
Status: Available from Firefox 47.
Object.values
returns an array of a given object’s own enumerable property values, like Object.keys
does for property keys, and Object.entries
returns an array of [key, value]
pairs.
Object.entries
is useful to iterate over objects.
createElement("img", { width: 320, height: 240, src: "http://localhost/a.png" }); // without Object.entries function createElement(name, attributes) { let element = document.createElement(name); for (let name in attributes) { let value = attributes[name]; element.setAttribute(name, value); } return element; } // with Object.entries function createElement(name, attributes) { let element = document.createElement(name); for (let [name, value] of Object.entries(attributes)) { element.setAttribute(name, value); } return element; }
When property keys are not used in the loop, Object.values
can be used.
- Specification Drafts
- Documentation
- Bugzilla Links
Object.getOwnPropertyDescriptors
Status: Available from Firefox 50.
Object.getOwnPropertyDescriptors
returns all own property descriptors of a given object.
The return value of Object.getOwnPropertyDescriptors
can be passed to Object.create
, to create a shallow copy of the object.
function shallowCopy(obj) { return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); }
- Specification Draft
- Documentation
- Bugzilla Link
String padding
Status: Available from Firefox 48.
String.prototype.padStart
and String.prototype.padEnd
add padding to a string, if necessary, to extend it to the given maximum length. The padding characters can be specified by argument.
They can be used to output data in a tabular format, by adding leading spaces (align to end) or trailing spaces (align to start), or to add leading "0"
to numbers.
let stock = { apple: 105, pear: 52, orange: 78, }; for (let [name, count] of Object.entries(stock)) { console.log(name.padEnd(10) + ": " + String(count).padStart(5, 0)); // "apple : 00105" // "pear : 00052" // "orange : 00078" }
- Specification Drafts
- Documentation
- Bugzilla Link
Trailing commas in function parameter lists and calls
Status: Available from Firefox 52 (now Beta, will ship in March 2017).
Just like array elements and object properties, function parameter list and function call arguments can now have trailing commas, except for the rest parameter.
function addItem( name, price, count = 1, ) { } addItem( "apple", 30, 2, );
This simplifies generating JavaScript code programatically, i.e. transpiling from other language. Code generator doesn’t have to worry about whether to emit comma or not, while emitting function parameters or function call arguments.
Also this makes it easier to rearrange parameters by copy/paste.
- Specification Drafts
- Bugzilla Link
Miscellaneous Changes
Remove proxy OwnPropertyKeys error with duplicate keys
Status: Available from Firefox 51.
The ownKeys
trap of a user-defined Proxy handler is now permitted to return duplicate keys for non-extensible object.
let nonExtensibleObject = Object.preventExtensions({ a: 10 }); let x = new Proxy(nonExtensibleObject, { ownKeys() { return ["a", "a", "a"]; } }); Object.getOwnPropertyNames(x); // ["a", "a", "a"]
- Specification Drafts
- Bugzilla Link
Case folding for \w, \W, \b, and \B in unicode RegExp
Status: Available from Firefox 54 (now Nightly, will ship in June 2017).
\w
, \W
, \b
, and \B
in RegExp
with unicode+ignoreCase flags now treat U+017F (LATIN SMALL LETTER LONG S) and U+212A (KELVIN SIGN) as word characters.
console.log(/\w/iu.test("\u017F")); // true console.log(/\w/iu.test("\u212A")); // true
- Specification Drafts
- Bugzilla Link
Remove arguments.caller
Status: Removed in Firefox 53 (now Developer Edition, will ship in April 2017).
The caller
property on arguments
objects, that threw a TypeError
when gotten or set, has been removed.
function f() { "use strict"; arguments.caller; // doesn't throw. } f();
- Specification Draft
- Bugzilla Link
What’s Next?
We’re also working on implementing ECMAScript proposals.
New Feature
Function.prototype.toString revision (proposal Stage 3)
Status: Work in progress.
This proposal standardizes the string representation of functions to make it interoperable between browsers.
- Proposal
- Bugzilla Link
Lifting Template Literal Restriction (proposal Stage 3)
Status: Available from Firefox 53 (now Developer Edition, will ship in April 2017).
This proposal removes a restriction on escape sequences in Tagged Template Literals.
If an invalid escape sequence is found in a tagged template literal, the template value becomes undefined
but the template raw value becomes the raw string.
function f(callSite) { console.log(callSite); // [undefined] console.log(callSite.raw); // ["\\f. (\\x. f (x x)) (\\x. f (x x))"] } f`\f. (\x. f (x x)) (\x. f (x x))`;
- Proposal
- Bugzilla Link
Async Iteration (proposal Stage 3)
Status: Work in progress.
The Async Iteration proposal comes with two new features: async generator and for-await-of
syntax.
The async generator is a mixture of a generator function and an async function. It can contain yield
, yield*
, and await
. It returns a generator object that behaves asynchronously by returning promises from next
/throw
/return
methods.
The for-await-of
syntax can be used inside an async function and an async generator. It behaves like for-of
, but interacts with the async generator and awaits internally on the returned promise.
async function* loadImagesSequentially(urls) { for (let url of urls) { yield await loadImage(url); } } async function processImages(urls) { let processedImages = []; for await (let image of loadImagesSequentially(urls)) { let processedImage = await processImageInWorker(image); processedImages.push(processedImage); } return processedImage; }
- Proposal
- Bugzilla Link
Conclusion
100% on the ES2016+ compatibility table is an important milestone to achieve, but the ECMAScript language will continue evolving. We’ll keep working on implementing new features and fixing existing ones to make them standards-compliant and improve their performance. If you find any bug, performance issue, or compatibility fault, please let us know by filing a bug in Bugzilla. Firefox’s JavaScript engine engineers can also be found in #jsapi on irc.mozilla.org. 🙂
fanboynz wrote on :
Hannes Verschore wrote on :
Adam wrote on :
mathieu wrote on :