JavaScript is constantly evolving! Since 2015, the language has added new features every year including some of our favorites like the rest and spread operator, destructuring and async/await.
Now that 2020 is here – it’s time to take a look at some of the latest and greatest additions to JavaScript. While this list does not include all of the new features, we’ve included some of the most common and helpful ones in your day to day programming, so let’s dig in!
1 – Optional Chaining
The new optional chaining syntax allows you to access properties on an object without worrying if that property exists or not.
It’s very common in JavaScript that you work with nested data structures like arrays and objects. As you might know, if you access a property in on an object, and that property does not exist, you always get back undefined
. So when working with data like this:
let instructorData = {
firstName: "Melissa",
funFacts: {
favoriteColor: "purple"
sayHi: function(){
return `Hi!`
}
}
}
You might find yourself writing something like instructorData.funFacts.sayHi()
. That works well, but what happens when you’re working with another instructor object and it’s possible that there might not be a sayHi
method. You might have to write code like this:
// imagine that instructorData is now an empty object
if(instructorData.funFacts.sayHi !== undefined){
instructorData.funFacts.sayHi();
}
We’re simply checking to see that our sub-object instructorData.funFacts
has a property on it called sayHi
. However, this still is not defensive enough and in our case, it will throw a TypeError
. That’s because our instructorData object (assuming it’s empty) does not have a property called funFacts so when we access anything on that property we will get a TypeError
!
So what happens if we’re unsure that we’ll even have a sub-object called funFacts
? We need to be defensive and would have to write code like this:
if(instructorData.funFacts && instructorData.funFacts.sayHi){
instructorData.funFacts.sayHi();
}
Here we make sure that we have a sub-object as well as a method on that sub-object and that can get messy quickly. To solve our problem we can use the new optional chaining syntax!
Instead of the if statement, we can place a ?
after the .
and JavaScript will either give us the property or return a value of undefined
. This works for nested data structures so that we don’t have to worry about a TypeError
if we access or modify properties on undefined
.
if(instructorData.funFacts?.sayHi){
instructorData.funFacts.sayHi();
}
And if we wanted to save the if statement we could even do instructorData.funFacts?.sayHi();
which instead of a TypeError
, it would return undefined
We’re simply asking JavaScript to check if there is a sub-object called funFacts
and if so, keep going, otherwise return undefined
. You can also use this syntax with bracket notation and arrays!
2 – Nullish Coalescing
Nullish coalescing adds the ability to check for values that are undefined
or null
and not just falsey values.
In JavaScript we have our falsey values – false
, 0
, NaN
, ""
, undefined
and null
.
In the case where you want to check if a value is null
or undefined
you can’t just do things like:
if(val)
The problem here is, what if val is a different falsey value like 0
or ""
? To get around this issue you can use the new nullish coalescing operator. If you want to see if a value is null-ish (undefined
or null
), this operator is quite helpful.
Here are some examples:
0 || null // null
0 ?? null // 0
"" ?? undefined // ""
"" || undefined // undefined
3 – Promise.allSettled
You might be familiar with the helpful Promise.all() method which accepts an array of promises and when resolved, returns an array of resolved values.
It’s a helpful way to make multiple asynchronous calls without blocking another call, and since it contains a “fail-fast” behavior, once a single promise is rejected, the whole things fails.
But what happens if you need to wait for all of the promises to finish regardless of whether they resolve or reject? Promise.allSettled to the rescue!
Promise.allSettled() will wait for all of the promises to be settled, regardless of whether they are resolved or rejected, and then it will resolve itself.
Let’s take a look at a small example.
let resolved = Promise.resolve(2);
let rejected = Promise.reject("oh no!");
let resolved2 = Promise.resolve(2);
Promise.allSettled([resolved, rejected, resolved2]).then(values => console.log(values));
/*
[
{status: "fulfilled", value: 2},
{status: "rejected", reason: "oh no!"},
{status: "fulfilled", value: 2}
]
*/
Notice here, we get back an array with three values where we can see the status of all of our promises and values for resolved ones and reasons for rejected ones. This can help with efficiently executing asynchronous tasks and then handling all of the rejected or fulfilled cases.
These features are all available in more recent versions of Chrome and in any modern web development workflow – so check them out and see where you can incorporate them in your day to day programming. Happy coding!