This post was taken from my blog, so be sure to check it out for more up-to-date content.
Last month - June 2019 - 10th edition of ECMA-262 standard was officially published. What does it mean? - Well, ECMAScript 2019 is here! The latest and greatest specification for JavaScript and other derivatives languages to follow. And while you may already have heard of some of its new features, we'll recall all of them! Plus some ECMAScript details and a sneak-peak of what's coming next!
Release cycle
While we already know what ECMAScript is, and how it's now-yearly release cycle works, there are some more interesting things to it.
Surely the release of every new ES specification is important, but it's recommended not to look at it as a once-per-year thing (unless you're living in pre-ES6 era). The specification is "alive", being worked on as you're reading this. Every release is just a summary of a bunch of new features. And that's how you should look at it - feature by feature.
You might know by now (possibly due to working with Babel before) that ECMAScript specification is created as a result of different proposals. Each proposal needs to go through 5 different stages...
Every new proposal starts its life as merely an idea, which needs to first be reviewed by TC39. This committee consists of experts and representatives of different companies/organizations (like Google and Mozilla) and decides about the future of the specification. When the submitted idea is positively reviewed, it goes to stage-0. From now on, it's public and available from the corresponding GitHub repo. As the proposal is discussed further and further at regular TC39 meetings, it either gets to the next stage or is abandoned in the process. Proposals at stage-3 can be considered to be very stable and assured. After the proposal reaches stage-4, it's official.
Now, all this process - different stages and stuff - doesn't necessarily match the implementation time. Many browser vendors in order to keep their browser ever-green and appealing to developers, often implement features even when they're still at the stage-3. Naturally, as such features are still non-standard, many people will most likely either use Babel or won't use the feature at all! But still, it's a fact. With that said, some features may even be implemented a while after the official specification drops. But, since the last "big" release of ES (ES6), such things don't take place very often.
ECMAScript 2019
With ES release cycle out of the picture, let's explore all new features of ES2019! I know that you might already have seen these before, but, please, let's bear with me for a moment as we're going to do this once more, in a beginner-friendly manner!
Array.prototype.flat{Map}()
As I mentioned earlier, many of ES2019 features can appear in browsers before the official specification drops. That's the case with two new immutable array methods - .flat()
and .flatMap()
.
Now, I've covered these methods a while back, so check that out if you want to learn more. Here's a little refresher!
const arr = [1, [2, [3, 4]]];
arr.flat(2); // [1, 2, 3, 4]
arr.flatMap(num => `${num}`); // ["1", "2,3,4"]
I think the naming of these methods reveal all there is to them. .flat()
can be used to flatten your multi-dimensional array. By default, it does it 1 level deep, but you can optionally pass an argument to specify the deepness, as we did in the example above.
Because calling .flat()
in combination with .map()
can be very useful, there's a special method for that too! - .flatMap()
. This guy maps the array first and then flattens it 1 level deep (no configuration here). And all of that comes with the improved performance of course!
Object.fromEntries()
With the introduction of Map
objects in ES6 and the need of representing the data with arrays in key-value fashion, Object.fromEntries()
can prove to be quite useful when doing such operations. It can change your Array or Map of key-value pairs (entries) into an actual object - something that before ES2019 required custom code.
const arr = [
["key1", "value1"],
["key2", 2],
["key3", {value: 3}]
];
Object.fromEntries(arr);
// { key1: "value1", key2: 2, key3: { value: 3 } }
String.prototype.trimStart() and more...
ES5 .trim()
method has 2 new colleges, or even 4! While .trim()
was removing unnecessary spaces from both sides of the string, now there're some new methods to do that form only the specified one!
.trimStart()
, also known as .trimLeft()
, can be used to trim the string from its start/left side...
const str = " str ";
str.trimStart(); // "str "
str.trimLeft(); // "str "
while .trimEnd()
and .trimRight()
can be used to do the same, from the right side.
const str = " str ";
str.trimEnd(); // " str"
str.trimRight(); // " str"
Optional catch binding
Apart from bringing us only a few new methods, ES2019 fixes some flaws of its predecessors. First one being the requirement to include error parameter in try
/catch
statements, even when not using it.
// then
try {
// ...
} catch(error) {
// ...
}
// now
try {
// ...
} catch {
// ...
}
The less code to write the better, right?
Function.prototype.toString();
In ES-based languages, you can .toString()
pretty much all the things! What its different calls will resolve in is a whole another story. But, up to ES2019, when used with functions, .toString()
returned a string with the function's code only. Now, it also takes into account comments, new lines, and whitespaces - everything!
function toBeStringified() {
// a comment
// a comment after new line
}
toBeStringified.toString();
/*
`function toBeStringified() {
// a comment
// a comment after new line
}`
*/
Symbol.prototype.description
Symbol
- always-unique data type used mostly as object's properties identifier - just got a new property called .description
. It can be used to access the Symbol
's optionally-passed description parameter, in the form of a string.
const mySymbol = Symbol(2);
mySymbol.toString(); // "Symbol(2)"
mySymbol.description; // "2"
Array.prototype.sort()
If you've used .sort()
before, you might know that it's a recommended practice not to rely on its default sorting algorithm. That's because there was no mention of any stability requirements for the algorithm, in the previous specifications. Now, in ES2019, that "stable sort" is required by specs, every JS engine needs to comply with it. That means that they still can use different algorithms, but there shouldn't be any misconceptions related to that. To see what I mean - check out the example below.
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 },
];
// Sort the dogs by `rating` in descending order.
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 },
]
*/
As you can see in the result comment above, the array is sorted "as expected" by our sorting function. In the original one - the items were sorted alphabetically by the name
field. Now, they're sorted by rating
first, but they're still in alphabetical order. The second fact is kind-of expected (sorted items should respect their previous positions), but it wasn't guaranteed before ES2019. From now on, new browsers will assure you the right order. But know that the old ones are still out there. That's why you should be very explicit when writing your sorting functions and not necessarily depend on the browser's default behavior too much.
A word on JSON...
JSON and JSON-related functionalities got a little revision too!
JSON.stringify()
now properly escapes Unicode "lone surrogates". This isn't such a big deal, considering that you might hardly ever encounter issues related to that. Still, it's one thing to think about less when writing your code. To give you an example:
// then
JSON.stringify('\uD800'); // "'οΏ½'"
// now
JSON.stringify('\uD800'); // "'\\ud800'"
There's yet another issue with Unicode strings that was fixed with ES2019! JSON is usually considered a subset of ECMAScript, but it wasn't entirely true up until this point. JSON strings could contain some unescaped Unicode characters (U+2028 and U+2029), while ES strings could not. Now, ES supports these characters, the issue is fixed, and JSON is truly a subset of ES.
What the future holds?
Now that we've covered all of the ES2019 goodness - what's next? New features are constantly being introduced to modern browsers engines (like V8), even when they're only at stage-3! Some of them are already scheduled for ES2020, others - not so much. But, I'd like to give you a small glimpse of the future which, in some cases, can be experienced right now! With that said, here's 3 most interesting features to look forward to in ES2020 and beyond!
Dynamic import()
From what I know, many developers still use code bundlers, even with ES modules being officially supported by all major browsers. But who can blame them? Modules really need some more "standardization" - not from the specification, but from the community. And for that, some time needs to pass and old browsers have to die...
But, so-called dynamic import()
is definitely going to part of this "modular future". Well... maybe future is a bit too bold of a term, considering that this feature has already been implemented by all major browsers and is at stage-4, scheduled for ES2020.
import("module.mjs")
.then((module) => {
module.default(); // export default stuff
module.doStuff(); // other stuff
});
The main advantage of dynamic import()
is the fact that loading modules is done in a lazy manner. In this way, you can significantly improve your app's performance, just by loading the necessary stuff first (with standard import
statements), and everything other later. Plus, they're almost unnoticeable in syntax when done with async
/await
!
(async () => {
const module = await import("module.mjs")
module.default();
module.doStuff();
})();
BigInt
Dealing with large numbers in JS can be pretty problematic. The range of possible values is big, but, in some cases, it's just not enough. And that's why BigInt
was born!
BigInt
is a completely new numeric primitive that works alongside currently-available Number
. It's already implemented in Chromium-based and some other browsers, but that's pretty much it. The support will definitely grow once BigInt
gets beyond stage-3 and becomes official seemingly with ES2020.
For now, until the support gets better, I think all you need to know is that you'll be able to express some big numbers in JS with great performance, without any 3rd-party library!
BigInt(Number.MAX_SAFE_INTEGER) + 2n; // 9007199254740993n
/* Value bigger than Number can represent
BigInts can be created with -n suffix */
Of course, as the name implies, BigInt
can only be used to represent integers. This makes me really look forward to possible BigDecimal (or something like that) in the future...
Private class fields
Probably one of the most awaited features of ES. Private class fields are something that many developers really want. The ability to hide the implementation details for real!
Private class fields are currently at stage-3. It's yet unsure if we'll see them in ES2020 or not. But, despite how awesome of a feature this might be, I still have some concerns about it. First, by current proposal, there's no mention of any kind of protected fields - those who use TypeScript or some other statically-typed language know what I'm talking about. And, what's even worst - the syntax for private fields in ES proposal is just... bad IMHO. The cross-language untold "standard" of public
, protected
and private
keywords is replaced by a hash (#
) symbol. Even though, I'm still happy that such feature (in any shape or form) is coming to JS!
class IncreasingCounter {
#count = 0;
get value() {
console.log("Getting the current value!");
return this.#count;
}
increment() {
this.#count++;
}
}
Want to know more?
ES specification and proposals are evolving every day. So do browsers and JS engines. If you want to be up-to-date with the latest and greatest features, I encourage you to check out v8.dev blog, where people behind V8 share very insightful information about the engine, its latest features and how they were implemented. Highly recommended!
So, that's it for this article. Consider following me on Twitter, on my Facebook page, or checking out my personal blog for more awesome content. And, as always, have a great day!
Top comments (5)
This is my first time seeing that hash symbol for private fields and...
... huh. I really don't like it at first glance! It's kinda ugly and unfamiliar.
However, having an unambiguous identifier built right into the name is pretty cool! No need to adopt special naming schemes to remind yourself if it's private data or not; no special knowledge that newcomers have to learn to navigate and understand like "it's common to use underscores to indicate private fields"
The description of .flatMap() is incorrect - it works like .map(), then .flat(1). That's a lot more useful, as it lets your mapping function return zero- or multiple-element arrays to change the number of elements in the result.
Thanks for the notice! I edited the article.
Private fields is a working on my Chrome 72 very well but I don't know maybe I turn on the flag and I don't remember.
In theory, public fields are available from v72, but private - only from v74. I don't know how this works for you. I can definitely confirm that mine v75 build supports this without any flags.