Back to top

JavaScript: useful features of ECMAScript 2019 reviewed

First of all let me apologise for not writing as frequent as I would’ve liked. It’s been a crazy busy couple of weeks and I had heaps of fun speaking at DDD Perth 2019.

That taken care of, this time I thought let’s go through the new features which are added into the ECMAScript 2019 (aka ES2019 or ES10), because they’re super exciting and make our lives much easier 😎.

TLDR;

At a glance, this version adds a few built-in functions on Array.prototype, flat and flatMap. Then we have Object.fromEntries for directly turning the return value of Object.entries into a new Object.

We also have trimStart and trimEnd on String.prototype over the widely used non-standard versions String.prototype.trimLeft and trimRight.

Another exciting feature is optional catch binding parameters. In addition to those, there are some JSON improvements, addition of Symbol.prototype.description which allows you to specify a description for your symbol, JSON.stringify returns well-formed UTF-8 regardless of input, and at last clarifying Function.prototype.toString by requiring that it either return the corresponding original source text or a standard placeholder.

So if you’re ready let’s dig in.

Array.prototype.{flat, flatMap}

flat() is a new method which let’s you create a one-dimensional array from a multi-dimensional one.

Imagine we have an array like below:

Copy
const myArray = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]];

Prior to flat if you wanted to achieve this, you had to do something like this:

Copy
const myNewArray = [].concat.apply([], myArray)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Or:

Copy
var myNewArray = myArray.reduce(
  function(prev,curr) {
    return prev.concat(curr)
  }
)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

With this new addition, it would be as simple as:

Copy
var myNewArray = myArray.flat(4);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

You can also chain multiple calls:

Copy
var myNewArray = myArray.flat().flat().flat().flat();
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

The argument into the flat function just specifies how deep it should look into the array. If you’re not sure how deep the array is, simply use Infinity as input:

Copy
var myNewArray = myArray.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

And if you don’t provide any input, by default it only goes one level deep:

Copy
var myNewArray = myArray.flat(); 
// [1, 2, 3, 4, 5, 6, Array(4)];

flatMap on the other hand, allows you to map each element using a mapping function and then flattens the result into a new array. Think of it as chaining a map function with a single flat. However, it can be really helpful both in terms of usage and efficiency of execution.

Copy
let myArray = [1, 2, 3, 4, 5];

let mappedArray = myArray.map(x => [x, x * 2]);
// [Array(2), Array(2), Array(2), Array(2), Array(2)]
// 0: (2)[1, 2]
// 1: (2)[2, 4]
// 2: (2)[3, 6]
// 3: (2)[4, 8]
// 4: (2)[5, 10]

let mappedFlattenedArr = mappedArray.flat();

// [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

let myNewArray = myArray.flatMap(v => [v, v * 2]);
// [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

String.prototype.{trimStart, .trimEnd}

These methods are fairly obvious as to what they will do for us. Just keep in mind that we had trimLeft which will be replaced by trimStart and trimRight which will be replaced by trimEnd.

Copy
let message = ' This is a string with white space around ';

message = message.trimStart();
// 'This is a string with white space around '

message = message.trimEnd();
// 'This is a string with white space around'

Object.fromEntries

This method get an Iterable and transforms key-value pairs to an object. But let’s see what is an Iterable:

JavaScript supports a protocol by which objects such as arrays can be used by control structures such as for…of and the spread operator ... to loop through data sequentially. This is referred to as the iterable and the data structures that support this functionality are called iterables. While JavaScript provides maps, arrays and sets with an iterable property from the get-go, plain objects do not have this by default.

To see this in action:

Copy
let entries = new Map([["name", "john"], ["age", 22]]);

let newObj = Object.fromEntries(entries);
// { name: 'john', age: 22 }

A real life use case is when parsing query strings:

Copy
let query = Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'));

// { foo: 'bar', baz: 'qux' }

Optional Catch Binding

Optional catch binding allows us to use try/catch without the error parameter inside the catch block.

Previously you had to use this syntax regardless of whether you cared about err or not, such as when you had to fall back to feature to support older browsers:

Copy
try {
  // try to use a web feature which may not be implemented
} catch (unused) {
  // fall back to a less desirable web feature with broader support
}

With ES2019 you can do:

Copy
try {
  // ...
} catch {
  // ...
}

Symbol.description

This new read-only description property is a string returning the optional description of Symbol objects. This method is encouraged to be used instead of Symbol.prototype.toString where it wasn’t obvious what will be returned.

Copy
let description = 'This symbol represents an emoji 😁';

let mySym = Symbol(description);
// Symbol('This symbol represents an emoji 😁')

console.log(mySym.description);
'This symbol represents an emoji 😁'

Function.toString

This method is a really useful one which returns the source code of a function. Imagine doing a troubleshooting on a block of code which is using a third party function. This can potentially help you to see the implementation (given it’s got source maps).

Copy
function myFn(emoji) {
  let msg = `${emoji} is hellav an emoji`;
  console.log(msg);
}

console.log(myFn.toString());

// "function myFn(emoji) {
//   let msg = `${emoji} is hellav an emoji`;
//   console.log(msg);
// }"

Of course this doesn’t work for everything. If we try it for map function on array:

Copy
Array.prototype.map.toString();

// "function map() { [native code] }"

Since the implementation is not written in JavaScript, it will just show you that this function is written in native code.

JSON.stringify

The team have done an improvement with Unicode characters and now this method can’t return malformed data.

Copy
// Non-BMP characters still serialize to surrogate pairs.
JSON.stringify('πŒ†')
// β†’ '"πŒ†"'
JSON.stringify('\uD834\uDF06')
// β†’ '"πŒ†"'

// Unpaired surrogate code units will serialize to escape sequences.
JSON.stringify('\uDF06\uD834')
// β†’ '"\\udf06\\ud834"'
JSON.stringify('\uDEAD')
// β†’ '"\\udead"'

Array.sort Stability

The team has decided to change the sort implementation so that it would be stable (that is, elements that compare equal must remain in their original order).

Copy
const grades = [
  { topic: 'math', grade: 10 },
  { topic: 'literacy', grade: 10 },
  { topic: 'chemical', grade: 13 },
  { topic: 'geography', grade: 12 },
  { topic: 'modern history', grade: 12 },
  { topic: 'art', grade: 13 },
  { topic: 'computer basics', grade: 14 },
  { topic: 'algebra', grade: 14 },
];

grades.sort(a, b => a.grade - b.grade);

// 0: {topic: "math", grade: 10}
// 1: {topic: "literacy", grade: 10}
// 2: {topic: "geography", grade: 12}
// 3: {topic: "modern history", grade: 12}
// 4: {topic: "chemical", grade: 13}
// 5: {topic: "art", grade: 13}
// 6: {topic: "computer basics", grade: 14}
// 7: {topic: "algebra", grade: 14}

Summary

In general JavaScript is moving in the right direction for helping developers write more stable, reliable and consistent code. You can find more information on their GitHub repo here.

Hope to see more awesome features in JavaScript and see you soon with another article.

Support my work πŸ‘‡πŸ½
Crypto