In the previous part of this article we discussed about ES2017 and ES2018 features. Let’s talk about the next versions to know more about them.

ES2019 has a very interesting feature set. Let’s have a look at them one by one.

1. Array.prototype.{flat,flatMap}

array.flat() method can be used to flatten a multidimensional array upto a given value.

The default value of the depth level is 1, i.e. if no value is specified. If we pass “Infinity” as a parameter inside the flat() function it will flatten the till the last depth level.

Example:

let arr = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]];

arr.flat(); // [1, 2, 3, 4, 5, 6, Array(4)];

arr.flat().flat(); // [1, 2, 3, 4, 5, 6, 7, 8, 9, Array(3)];

arr.flat(3); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

arr.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

array.flatMap() is a blend of flat() with map() (an ES6 feature). The flatMap() method maps each element using a mapping function and then flattens the result into a new array using the flat() method.

Here’s an example:

let arr = [1, 2, 3, 4, 5];

arr.map(x => [x, x * 10]);
// [Array(2), Array(2), Array(2), Array(2), Array(2)]
// 0: (2)[1, 10]
// 1: (2)[2, 20]
// 2: (2)[3, 30]
// 3: (2) [4, 40]
// 4: (2) [5, 50]


arr.flatMap(v => [v, v * 2]);
// [1, 10, 2, 20, 3, 30, 4, 40, 5, 50]

2.Optional catch binding

ES2019 will allow the exclusion of the catch binding in cases where the binding is not needed.

There are many scenarios where we do not need  the parameter bound to the catch block of a try/catch.

Earlier, we had to do the following:

try {  
//…
} catch (e) {  
//handle error
}

Now, we can now simply drop it:

try {  
//…
} catch {  
//handle error
}

3. Object.fromEntries()

We have already seen the Object.entries() method in ES2017.

Now the Object.fromEntries() method in ES 2019, can return a new object from an array of key-value pairs. This method only accepts iterables.

let entries = new Map([["name", "maria"], ["age", 28]]);

console.log(Object.fromEntries(entries));
// { name: 'maria', age: 28 }

const person = { name: 'gisele', age: 21 }
const entries = Object.entries(person)
const newPerson = Object.fromEntries(entries)
person !== newPerson //true 

4. String.prototype.{trimStart,trimEnd}

trimStart() and trimEnd() are methods that can be used to return the string removing the whitespace from its start and/or end.

For keeping a uniformity with the methods like padStart() and padEnd(), the standard name is trimStart() and trimEnd(). Although, for web compatibility reasons, trimRight() and trimLeft() remains as an alias to trimStart() and trimEnd().

So, for some engines:

String.prototype.trimRight.name === "trimEnd";
String.prototype.trimLeft.name === "trimStart";

Let’s see an example:

var dummyString = '   foobar   ';
console.log(dummyString.length); //returns 12
var str = dummyString.trimEnd();
console.log(str.length); //returns 9
console.log(str);        //   foobar
str = dummyString.trimStart();
console.log(str.length); //returns 9
console.log(str);        //foobar   
str=str.trimEnd()
console.log(str)         //foobar

5. Symbol.prototype.description

This is a read-only property of Symbol Objects which returns the description in a string format, instead of having to use the toString() method. This can be used for debugging purposes.

let mySymbol = `My Symbol`;

let symObj = Symbol(mySymbol);

console.log(symObj) // Symbol(mySymbol);

console.log(String(symObj) === `Symbol(${mySymbol})`); // true

console.log(symObj.description); // "My Symbol"

6. JSON improvements

The reason of this improvement was that the line separator (\u2028) and paragraph separator (\u2029) symbols were not allowed in ES strings, you would have to use an escape sequence to put them into a string.

const foo = '"\u2028"';
const bar = '"\u2029"';

//EARLIER
eval(foo); // SyntaxError
eval(bar); // SyntaxError

//NOW
eval(foo); // returns symbol
eval(bar); // returns symbol

But, JSON string literals could contain the same,

Hence, it was decided to take away the restriction for ECMAScript string literals. This simplified the grammar of the specification, because the rules for ECMAScript string literals and JSON string literals remain consistent.

7. Well-formed JSON.stringify()

This was submitted to prevent JSON.stringify from returning abnormal Unicode strings. The proposed solution is to represent unpaired surrogate code points as JSON escape sequences rather than returning them as single UTF-16 code units.

Before this change calling JSON.stringify() would return a malformed Unicode character (a “�”).

Now those surrogate code points can be safely represented as strings using JSON.stringify(), and transformed back into their original representation using JSON.parse().You can read about this feature in detail here.

8. Function.prototype.toString()

We already have the toString() method within the function prototype before but in ES2019 it was revised and include the comment that is within the function but note that it doesn’t work on Arrow Functions.

Here is a small example for you:

function /* comment */ foo /* another comment */ (){}
 
// BEFORE
console.log(foo.toString()); // function foo(){}
 
// NOW ES2019
console.log(foo.toString()); // function /* comment */ foo /* another comment */ (){}
 
 
// Arrow Syntax
const bar /* comment */ = /* another comment */ () => {}
 
console.log(bar.toString()); // () => {}

So that was all about the new features introduced in ES10 or ECMAScript 2019. The committee, TC39 has started working on the successor which is ECMAScript 2020 or ES11, however you like to call it. Let’s have a look into the proposed features of ES11 expected to be released by the summer of year 2020.

1. String.prototype.matchAll()

The first feature of ECMAScript to reach stage 4 is String.prototype.matchAll() proposed by Jordan Harband.

String.prototype.matchAll() method. It behaves similarly to match(), but returns an iterator with all regular expression matches in a global or sticky regular expression. This provides a simple way to iterate over matches, typically when you want access to capture groups.

The method match() works fine on all the cases, unless you’re trying to return global matches with capturing groups.

Example:

const regex = /t(e)(st(\d?))/g;
const string = 'test1test2';
const results = string.match(regex);
console.log(results);
//returns ['test1', 'test2']

When you run this in a console you will notice that it returns an array containing the strings ‘test1’ and ‘test2’.

Now if you remove the g flag from the regular expression you will get capturing groups, but its only the first match. It would look like the following:

// REMOVE THE g FLAGE FROM THE CODE ABOVE
['test1', 'e', 'st1', '2', index: 0, input: 'test1test2', groups: undefined]

This string contains a second possible match beginning with ‘test2’ but you get it.Now to get all of the capturing groups for each match we can use String.prototype.matchAll(), the proposal shows two practicable approaches.

const regex = /t(e)(st(\d?))/g;
const string = 'test1test2';
const matches = string.matchAll(regex);
for (const match of matches) {
  console.log(match);
}
// RETURNS

// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', groups: undefined]
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', groups: undefined]

There are a few things to notice here. match() returns an array on a global search, whereas, matchAll() returns an iterator that works gracefully with for…of loops. The iterator produces an array for each match, including the capturing groups.

2. Import () – dynamically importing ES modules

The second feature of ECMAScript to reach stage 4 is String.prototype.matchAll() proposed by Domenic Denicola.

ECMAScript modules are entirely static. User must specify what you import and export at the time of compiling and it cannnot react to changes at runtime.
The static structure of imports is implemented syntactically in two ways. Consider the following example:

import * as someModule from './dir/someModule.js';
  • This import declaration can only appear at the top level of a module,which  prevents you from importing modules inside an if statement or inside an event handler.
  • The module specifier ‘./dir/someModule.js’ is constant; you can’t compute or change it at runtime through a function call or anything.

The proposal enables dynamic module imports.

const moduleSpecifier = './dir/someModule.js';
import(moduleSpecifier)
.then(someModule => someModule.foo());

Although it works like a function, import() is an operator. To be able to resolve module specifiers relatively to the current module, it needs to know from which module it is invoked. Normal functions cannot resolve that..

Some of the use cases of dynamic import() are like on demand loading, conditional loading modules, computed module specifiers etc.
There are a few more features which are presently in Stage 3 and are expected to be in Stage 4 of the TC39 process very soon. Here’s a list of the proposals in Stage 3.

ProposalAuthor
Promise.allSettledJason WilliamsRobert PamelyMathias Bynens
Numeric separatorsSam GotoRick Waldron
Top-level awaitMyles Borins
WeakRefsDean Tribble

That was all for this article, stay tuned for more updates on the ES releases.