After the backlog of proposals dealt within ECMAScript 2015, the annual updates have tended to be smaller, with a mix of new features, syntax improvements and updates that cover slight omissions in the standard.
ECMAScript 2019 has some of those updates to clean up the language internally, including small syntax tweaks making it a release that’s about language correctness with some improvements for developer productivity.
Common Sense and Stability in ECMAScript 2019
There are new trimStart and trimEnd methods to remove white space before or after a string: if you’ve been using trimLeft and trimRight for this the new methods are less confusing for right-to-left languages. There’s a new built-in way to change an array of key-value pairs to an object, Object.fromEntries, rather than having to write extra code to do that.
The ECMAScript specification now requires that Array.prototype.sort is a stable sort: given the same input array, sorting will always produce the same output array, even if there are two identical items in the set with the same sort position.
This makes new code simpler to write, but can affect old code; mostly it means you won’t need logic you put in to make sort ordering constant and you might have to update your tests.
Optional catch binding when writing try/catch statements is a small, common-sense syntax change that will remove a lot of developer frustration because previously you’ve had to include code you don’t really need, just to avoid errors from ESLint and TypeScript. “Previously you couldn’t omit the catch binding [for an error parameter] even if you didn’t care about it, so you’d have to deal with linting problems because you declared a catch variable that you then didn’t use — because you didn’t care about it.”
The new flat and flatMap methods may be the most useful change in ECMAScript 2019. Flat was originally suggested as “flatten” because it flattens an array (if any of the values in the array are themselves arrays, the values in them are extracted into the top-level array) but that conflicted with the widely used MoTools library. flatMap first iterates over the array (map) and then flattens it (flat) in case the map operation has created a nested array (the name makes more sense when you remember that it’s borrowed from functional languages where it would be written as flat(map(…)), Terlson tells us).
ECMAScript 2020 Goes Big on Numbers
In terms of features, he called ECMAScript 2020 is a much bigger update than 2019. “We have a lot more syntax proposals in there, and we have big things like BigInt.”
“That makes it really frustrating when you’re writing libraries that target both Node.js and browsers,” Terlson said.
It proved surprisingly difficult to find a name that didn’t conflict with code that’s already in use, which is why the name is globalThis rather than just global.
Chaining and Coalescing
ECMAScript 2020 also adds some powerful operators that are already in languages like Swift and C# with syntax that simplifies some common tasks.
“Optional chaining lets you say, ‘if this object exists, then I want to access this property, otherwise just gives me undefined’ and it prevents you from getting the ‘undefined is not an object’ error. It turns multiple lines of spaghetti code into one line of just ‘?, ?, ?.’ And this comes up all the time, especially when you’re dealing with like JSON responses from a service where a record might be present or not: you just want to get it if it’s there and if it’s not, you want it to be undefined right. This is a feature that developers have been clamoring for a number of years and we finally delivered it,” Terlson suggested.
Drop in the nullish coalescing operator and you only get the default value if what’s returned is either undefined or null, giving you a better way of applying defaults.
Making More Promises
ECMAScript 2021 may include the ability to use await in the root of your program, instead of having to write an async function to put it in, but ECMAScript 2020 will extend import() so that you can load a module asynchronously, which means that you can do conditional or lazy loading by getting a promise for the module, and not load any modules that aren’t actually needed. This is so useful that the main browsers already support it.
“Dynamic import is useful for polyfilling because sometimes you need to check the environment to see if you need a polyfill, and if you do you can go ahead and import it. You can also do things like localized modules, ‘foo module-en’ for English and ‘foo module-sp’ for Spanish, where you get the user locale and then use dynamic import to load the appropriate module.”
Promise.allSettled is a useful addition to the two Promise combinators added in ECMAScript 6. Promise.all resolves if all the promises you pass into it complete successfully and rejects as soon as the first promise is rejected. Promise.race returns the first promise to get fulfilled, whether it succeeds or not (winning the race). But suppose you want to get the results of all the promises you pass in, even if one of them fails? Promise.allSettled waits for all the promises to be settled but it doesn’t matter whether they’re resolved or rejected: it returns an array with all the results.
There’s a fourth option found in a lot of promise libraries, Promise.any, which is the inverse of Promise.all, returning the first promise to succeed or list of the error codes if they all fail; that’s useful if any of the promises will do – but you need to know what went wrong if none of them succeed. That’s currently a stage 3 proposal, so it might be ready for ECMAScript 2021.
One final ECMAScript 2020 feature fixes something that’s been confusing developers for many years with another of those small language improvements that should remove enormous amounts of frustration when working with regular expressions.
When you call the current String.prototype.match the result depends on whether the regex you pass in has the -g global flag or not. Developers have been finding that confusing for Plus, if you pass in a string that you want to match, it will only ever march one instance of the string, but finding all the occurrences of a string is a very common use case (and running code to do that without the global flag can produce an infinite loop).
The way match works with the global flag doesn’t change, but String.prototype.matchAll makes the process much more explicit, Telson explained. ”If you pass in a regular expression without the global flag it gives you an error right away. ‘This doesn’t make sense: you’ve told me to match all but you’ve given me a regex that only matches one thing.’ So that helps users use the API correctly, But the biggest benefit is if you pass in a string, it will match all occurrences of that string and you get an array of all the results.”