# The future of JavaScript (ECMAScript 2019 and beyond)

Source: https://tpiros.dev/blog/the-future-of-javascript-ecmascript-2019-and-beyond

The [TC39](https://tc39.es) committee (consisting of members from organisations such as Google, Microsoft and Mozilla) reviews proposals as they're submitted. Proposals then move through 4 stages, where reaching stage 4 means it'll officially be added to the JavaScript language.

> You can visit the committee's website to see proposals that reached Stage 3 ("close to completion"). For every other proposal, check the [TC39 GitHub repository](https://github.com/tc39/proposals).

> During [Google I/O 2019](https://youtu.be/c0oy0vQKEZE), these proposals were shared. This article summarises those announcements.

> Some of the items discussed here already have Stage 4 (final) status, meaning they'll be released soon.

# Array.prototype.flatMap()

If you've worked with JavaScript before, you've likely run into the `flat()` method which flattens an array. This method accepts a parameter where you can specify the level to flatten. Passing in `Infinity` will recursively flatten until there are no more nested arrays:

```javascript
const arr = [1, [2, [3]]];
arr.flat(); // [1, 2 [3]]
arr.flat(Infinity); // [1, 2, 3]
```

We also frequently call the map function to perform operations on elements in an array:

```javascript
const duplicate = (x) => [x, x];

[2, 3, 4].map(duplicate); // [[2, 2], [3, 3], [4, 4]]
[2, 3, 4].map(duplicate).flat(); // [2, 2, 3, 3, 4, 4]
```

This pattern crops up so often that `flatMap()` was added to the language. It's not just a convenience method; it performs better than running `map()` and `flat()` separately:

```javascript
[2, 3, 4].flatMap(duplicate); // [2, 2, 3, 3, 4, 4]
```

# Object.fromEntries()

`Object.entries()` returns an array of the key-value pairs of an Object (first element is the key, second is the value). `Object.fromEntries()` reverses this operation and lets you reconstruct an object from a nested array:

```javascript
const obj = { x: 42, y: 1 };
const entries = Object.entries(obj); // [['x', 42], ['y', 1]]

for (const [key, value] of entries) {
  console.log(`The value of ${key} is ${value}.`);
}
// The value of x is 42.
// The value of y is 1.

// reconstruct the object from "entries":
const newObj = Object.fromEntries(entries); // { x: 42, y: 1 }
```

This is especially useful for transforming objects. Because `Object.entries` returns an array, you can use array operations to manipulate the dataset, then easily reconstruct the object using `Object.fromEntries()`:

```javascript
const obj = { x: 42, y: 1, hi: 10 };
const entries = Object.entries(obj); // [['x', 42], ['y', 1]]

const newObj = Object.fromEntries(
  entries
    .filter(([key, value]) => key.length === 1)
    .map(([key, value]) => [key, value * 2])
); // { x: 84, y: 2 }
```

`Object.fromEntries()` also works on JavaScript maps:

```javascript
const obj = { x: 42, y: 1 };
const map = new Map(Object.entries(obj));
const objectCopy = Object.fromEntries(map);
```

This becomes handy when you're working with maps in your code, but need to serialise that map data to JSON for an API, or pass the data as an object to another library instead of a map.

# Global This

Polyfills and libraries may need access to the global `this`. But depending on where the JavaScript code executes, the global object could be `self`, `window`, `global` or `this`. You'd end up writing a lengthy piece of code just to check for the existence of a global object. Something like this might help the code execute in the browser, in Node.js, or in a Service Worker:

```javascript
const getGlobalThis = () => {
  if (typeof self !== 'undefined') return self; // service & web workers
  if (typeof window !== 'undefined') return window; // browsers
  if (typeof global !== 'undefined') return global; // Node.js
  if (typeof this !== 'undefined') return this; // standard JavaScript shell
  throw new Error('Unable to locate global object');
};
const theGlobalThis = getGlobalThis();
```

That code has flaws (think about a module bundler wrapping your code, where `this` may not refer to the `this` you intended). And in `strict` mode, `this` is `undefined` from the start.

The solution? `globalThis`. It hands you the global object regardless of where and how the code runs:

```javascript
const theGlobalThis = globalThis;
```

# Stable sort

The `sort()` method in JavaScript used to be unreliable. Here's a typical example:

```javascript
const dogs = [
  { name: 'Abby', rating: 12 },
  { name: 'Bandit', rating: 13 },
  { name: 'Choco', rating: 14 },
  { name: 'Daisy', rating: 12 },
  { name: 'Elmo', rating: 12 },
  { name: 'Falco', rating: 13 },
  { name: 'Ghost', rating: 14 },
];
```

By default the values are stored alphabetically, but what if we want to sort by rating?

```javascript
dogs.sort((a, b) => b.rating - a.rating);
/*
[ { name: "Choco",  rating: 14 },
  { name: "Ghost",  rating: 14 },
  { name: "Bandit", rating: 13 },
  { name: "Falco",  rating: 13 },
  { name: "Abby",   rating: 12 },
  { name: "Daisy",  rating: 12 },
  { name: "Elmo",   rating: 12 }]
*/
```

The results are sorted by rating, and dogs with the same rating keep their original alphabetical order.

But until recently, JavaScript didn't guarantee stable sort for arrays. The result could have come back differently, and developers couldn't rely on the order. Some JavaScript engines used a stable sort for short arrays and an unstable sort for larger ones.

Array sort is now stable regardless of the array size.

# Promises

We can already use `Promise.all()` and `Promise.race()`. `Promise.all()` short-circuits when an input value is rejected. `Promise.race()` short-circuits when an input value is settled.

Two new proposals extend these: `Promise.allSettled()` doesn't short-circuit at all (all values must settle to either fulfilled or rejected). `Promise.any()` short-circuits when an input value is fulfilled, signalling as soon as one of the promises succeeds.

# Internationalisation

The `Intl` API has picked up new features to enable better localised support for websites.

## Intl.RelativeTimeFormat

This helps developers implement locale-aware relative time formatting such as 'yesterday' or '10 minutes ago'.

```javascript
const rtf_en = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const rtf_es = new Intl.RelativeTimeFormat('es', { numeric: 'auto' });
rtf_en.format(0, 'day'); // today;
rtf_es.format(0, 'day'); // hoy
rtf_en.format(-1, 'day'); // yesterday;
rtf_es.format(-1, 'day'); // ayer
```

## Intl.ListFormat

Sometimes, when working with a list, you want to output a sentence with the elements joined by the appropriate connecting word (such as "and"):

```javascript
const lf_en = new Intl.ListFormat('en');
lf_en.format(['Susan', 'John', 'Jack']); // Susan, John and Jack

const lf_es = new Intl.ListFormat('es');
lf_es.format(['Karol', 'James', 'Jose']); // Karol, James y Jose
```

You can also pass in options and add a disjunction type:

```javascript
const lf_en = new Intl.ListFormat('en', { type: 'disjunction' });
lf_en.format(['Susan', 'John', 'Jack']); // Susan, John or Jack

const lf_es = new Intl.ListFormat('es', { type: 'disjunction' });
lf_es.format(['Karol', 'James', 'Jose']); // Karol, James o Jose
```

# Private fields

In JavaScript, there's been no way to make a field in a class truly private. Conventions exist (like prefixing with `_` to signal a "private" member), but the language still treats it as public.

With ES2019, you can use the `#` symbol to mark class members as private. The language itself enforces this; they can't be accessed outside the class body.

```javascript
class Counter {
  #count = 0;
  get value() {
    return this.#count;
  }
  increment() {
    this.#count++;
  }
}
```

Class fields also open another door. Say you want to add a new property to a subclass. Normally, you'd do this in the constructor:

```javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
}
class Dog extends Animal {
  constructor(name) {
    super(name);
    this.hasLongTail = false;
  }
  bark() {
    // ...
  }
}
```

Now you can replace that with the class field syntax:

```javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
}
class Dog extends Animal {
  hasLongTail = false;
  bark() {
    // ...
  }
}
```

> Private methods, getters and setters are also planned.

# Large numeric literals

Numeric separators let developers group numbers by thousands for readability:

```javascript
let budget = 1_000_000_000_000;
// What is the value of `budget`? It's 1 trillion!
// Let's confirm:
console.log(budget === 10 ** 12); // true
```

# BigInt

`BigInt` is a new primitive that gives JavaScript a way to represent numbers larger than 2^53. Look at the first example below: the calculation is wrong because we should get a number ending with 7, yet we get something unexpected. `BigInt` values are created by appending `n` at the end of the number. Multiplying those numbers together produces the correct result:

```javascript
1234567890123456789 * 123; // 151851850485185200000 (incorrect)
1234567890123456789n * 123n; // 151851850485185185047n (correct)
```

`BigInt` can be formatted correctly using the `toLocaleString()` method as well as the International Number Format, just as you'd do with a regular number:

```javascript
1234567890123456789n.toLocaleString('es');
const nf = new Intl.NumberFormat('fr');
nf.format(1234567890123456789n);
```

> `BigInt` can't be mixed with other types (i.e. `1234567890123456789n * 123` would cause a TypeError).

# Top level `async/await`

`async/await` isn't new, but the two keywords go hand in hand. Using `await` is only possible inside an `async` function, which means code like this pops up everywhere:

```javascript
(async () => {
  const result = await somethingAsync();
  somethingElse();
})();
```

A new proposal enables top level `await`:

```javascript
const result = await somethingAsync();
somethingElse();
```

> Chrome DevTools already lets developers do this, but it wraps the entire function in an `async` call behind the scenes.

# Conclusion

Since ES6 / ECMAScript 2015, JavaScript has been getting more frequent updates. That means developers can tap into significant new features sooner. Some of the features mentioned here are already supported by various browser platforms or Node.js. For those that aren't, polyfills are generally available. Give these new features a spin.
