JavaScript: New Features
There are some new ECMAScript proposals that have landed in the “stage 4” of the TC39 proposal track. Let’s have a quick look at these features and experiment with them.

As we know, when an ECMAScript proposal reaches stage 4, it is ready to be included in the ECMAScript standard and browsers can start implementing it in their JavaScript engines. The following table shows the stages a proposal has to go through until it is finally landed in the hands of the public.
In 2019, I published the article “What’s new in JavaScript: Google I/O 2019 Summary” about the new JavaScript featured landed in 2019. Not sure if I can call it “popular” but it gained some good traction among JavaScript developers and I thought, why not do the same for the year 2020.
Though Google I/O 2020 has been canceled, it doesn’t stop the ECMAScript (JavaScript standard) development process. This year, we are expected to see some cool new JavaScript features coming to browsers and Node. Some of these features are pretty new and might not be available in your browser, so use Firefox 80 or Chrome 85 to test them out or use Babel plugins.
Optional Chaining
We have abused && operator to get around a few sneaky things. As we know, JavaScript objects can be nested, and accessing the nested property isn’t always runtime safe operation. Let me show you how.
In the above example, the object obj is a plain JavaScript object but it is deeply nested. So accessing the value of the property p would need the traversal of it obj > a > b > c > p. Let’s what happens if any path is missing.
▶ obj.a.b.c.p
◁ 1
▶ obj.a.b.d.p
◁ undefined
▶ obj.a.b.e.p
◁ Uncaught TypeError: obj.a.b.e is null
▶ obj.a.b.f.p
◁ Uncaught TypeError: obj.a.b.f is undefined
So in the first case, accessing obj.a.b.c.p returned 1 because it’s a valid path. Accessing property p on obj.a.b.d was also a success since obj.a.b.d is a number which also kind-of an object as we can access properties on it.
However, we can’t access properties on null and undefined since they are not objects. These together are called the nullish value. Whenever you try to access properties on a nullish value, JavaScript will throw a TypeError.
So how can we circumnavigate this? Well, we could use if/else statement to check if a property is non-nullish and travel deep.
if(obj.a){
if(obj.a.b){
if(obj.a.b.f){
console.log( obj.a.b.f.p );
}
}
}
In the above snippet, we are checking if every ancestor of the property p is not nullish. Since obj.a.b.f would return undefined, the inner console.log statement would not be evaluated, therefore no TypeError.
However, there is a more popular way to achieve the same.
▶ obj.a && obj.a.b && obj.a.b.c && obj.a.b.c.p
◁ 1
▶ obj.a && obj.a.b && obj.a.b.f && obj.a.b.f.p
◁ undefined
▶ obj.a && obj.a.b && obj.a.b.e && obj.a.b.e.p
◁ null
The Logical AND operator (&&) in JavaScript works in a very unexpected manner as you can see above. JavaScript evaluates the expressions separated by && from left to right until one expression returns a falsy value. At that point, JavaScript will no longer evaluate the next expressions and return the value of the entire expression (line) would be that falsy value.
If however, no expression returns a falsy value, then the returned value would be the value returned by the last expression. Hence, the first expression (line) returned 1 and the last expression obj.a.b.c.p was evaluated. However, the second expression (line) returned undefined, since obj.a.b.f returns undefined (as property f is missing on b) which is a falsy value. You can apply the same logic for the third expression (line).
💡 In JavaScript, a value falsy value when it is passed to the Boolean function and the expression returns false. Therefore, false, 0, undefined, null, "" and NaN are falsy values. When a falsy value appears in if statement (parenthesis), that if block won’t be evaluated.
However, there is a huge problem with using if statement as well as && operator to safely access nested properties. Since if statement as well as the && operator short-circuits after falsy values, we can’t access properties on an empty string ("") or 0 since they are falsy. Let’s take an example.
In the above example, the value of property d and e are falsy values, however, we should be able to access properties on them.
▶ obj.a && obj.a.b && obj.a.b.d && obj.a.b.d.length
◁ ""
▶ obj.a && obj.a.b && obj.a.b.e && obj.a.b.e.toFixed
◁ 0
Well, this was a disappointment. Every string has the length property that returns the number of characters (UTF-16 code units) in that string while every number has toFixed property which is a function to format the number. The first expression (line) returns "" because obj.a.b.d evaluated to "" which is falsy and the second expression (line) returns 0 because obj.a.b.e evaluated to 0 which is also falsy.
So what’s the solution. Well, JavaScript now supports optional chaining (.?) operator. We can use this operator in the place of . while accessing the property on an object. For example, obj?.a?.b?.f?.p returns undefined.
The .? operator checks if the previous value (on its left side) is nullish (null or undefined). If it is, then it short-circuits the whole expression and returns undefined without moving forward. If not, then it will keep moving forward until some nullish value is encountered, else the property value is returned.
▶ obj?.a?.b?.c?.p
◁ 1
▶ obj?.a?.b?.d?.p
◁ undefined
▶ obj?.a?.b?.e?.p
◁ undefined
▶ obj?.a?.b?.f?.p
◁ undefined
Now in the above example, we did not encounter any TypeError exceptions since JavaScript did not execute e?.p and f?.p parts of the above expression since the property e is null and the property f is undefined (nullish).
We do not necessarily need to replace every . with ?. but that could lead to the same problem as before. For example, obj?.a.b?.f?.p would technically work in our case, but it would fail when the property b doesn’t exist on a.
When it comes to uses cases, there are many possibilities with the optional chaining operator (?.) just beside accessing the property safely. Let’s understand different syntax and different uses cases.
▶ obj?.a?.['b']?.c?.p
◁ 1
In the above case, we used square-bracket notation ?.['b'] instead of simply ?.b. This could be useful when the property name is stored in a variable or returned from an expression such as a function call.
💡 Since ?. do not evaluate any expression on its RHS if the LHS value is nullish, any expression in the square-brackets will not be evaluated. For example, the i in obj?.a?.[++i] will not be incremented if obj or obj.a is nullish.
We can also use this operator to conditionally execute a method (function property) if that method exists on the object. For example, the method b will only execute in the expression obj?.a?.b() if a is not nullish. However, if b is nullish, then it will result in a TypeError exception. For that, we use the obj?.a?.b?.() expression. So the function call is only made when b is not nullish (but results in TypeError if b is not a function).
Though it wasn’t the intension of this proposal, but we can use delete operator in conjugation with the optional chaining operator to achieve some cool effects. Since delete operator evaluates an expression that should return a property reference, the optional chaining operator can conditionally provide that reference for non-nullish properties.
⊙ var obj = {};
▶ delete obj.a.b
◁ Uncaught TypeError: obj.a is undefined
▶ delete obj?.a?.b
◁ true
The first expression delete obj.a.b would attempt to delete b even if the value of property a is nullish which would return in a TypeError. But the second expression does not throw and returns true since obj?.a?.b doesn’t return a property reference.
Though it might sound tempting to do something like new Person?.() or obj?.a?.b = 3, but these expressions are not valid. This proposal has explicitly mentioned these points in the proposal document, check here.
🐞 [proposal] [support] [babel-plugin]
Nullish Coalescing Operator
Like the && operator (Logical AND), we have || operator which is called Logical OR operator since it performs logic OR operation between LHS and RHS. As && operator short circuits when a falsy value found and returns the value of the last executed expression, the || operator short circuits when a truthy value is found and returns the value of the last expression, else the value of the last expression is returned.
💡 A truthy value is a value that is not falsy.
▶ 0 && "hello" & 1
◁ 0
▶ 0 || "hello" || 1
◁ "hello"
▶ 0 || "" || NaN
◁ NaN
In the above example, the first expression (line) returns 0 as the first value is falsy so the && operator short circuits and returns that value. However, in the second case (line), || operator does not short circuit until "hello" since it is a truthy value. In the third example, the last falsy value is returned.
This is all well and good. But the problem comes when we want to use it to fall back to a default value. For example, let’s imagine you have some uninitialized variables and you want to select one of them.
⊙ i = undefined, j = undefined;
▶ i || j || 1
◁ 1
⊙ j = 0
▶ i || j || 1
◁ 1
So let’s talk about the problem. Here in the example above, we have defined variables i and j with the initial value of undefined. So basically, i and j are falsy at the moment and nullish at the same time since they are undefined. Here, 1 is acting as the default value if i and j are undefined.
So the result of the expression i || j || 1 returns the value of 1 (last value) since i and j are undefined. However later down the program, we assigned 0 to j, so technically speaking, j has a value now. However since 0 is falsy, just like undefined, the expression i || j || 1 still returns the same result (last value of 1). Ideally, we need the return value of 0.
This problem is more apparent when we want to fall back on a default value when an object property is missing or its value is nullish.
In the above example, property d and e are falsy but they exist on the nested object c and they are not nullish. However, they are falsy. Why that is important? I will come to that. Also, the property f is not present in c, so accessing f on c would return undefined which is a nullish value.
▶ obj?.a?.b?.c || "default"
◁ {p: 1}
▶ obj?.a?.b?.c?.p || "default"
◁ 1
▶ obj?.a?.b?.f || "default"
◁ "default"
▶ obj?.a?.b?.f?.p || "default"
◁ "default"
▶ obj?.a?.b?.d || "default"
◁ "default"
▶ obj?.a?.b?.d?.p || "default"
◁ "default"
▶ obj?.a?.b?.e || "default"
◁ "default"
Let’s hold our breath and go through these expressions one at a time.
- The obj?.a?.b?.c || “default” returns {p: 1} because obj?.a?.b?.c expression returns {p: 1} and since this is a plain JavaScript object, it is a truthy value and || operator short circuits and returns it.
- The same logic can be applied to the obj?.a?.b?.c?.p property path does exist and its value is 1 which is a truthy value. Therefore, the expression obj?.a?.b?.c?.p || “default” returns 1 back.
- The obj?.a?.b?.f and obj?.a?.b?.f?.p path doesn’t exist. Therefore, the optional chaining operator ?. safely returns undefined and undefined || "default" returns "default" since undefined is a falsy value and "default" is a truthy value (not that it matters in this case).
- Cool! Well, hold on. The expression obj?.a?.b?.d || “default” returns "default". That’s expected as obj?.a?.b?.d returns "" (empty string) which is falsy but it’s a problem in practical use cases. I want to fall back to a default value only when the property is missing or nullish.
- The same logic is applied to obj?.a?.b?.e since it returns is 0 which is falsy value. If I set e to false and use the expression obj?.a?.b?.e || true, it would return true as the value false is falsy.
This is a huge problem and if you are a seasoned JavaScript developer then you know how problematic this can get. To circumnavigate this, we need to use all the tricks up our sleeves but we now have a native solution.
The ?? operator called the Nullish Coalescing Operator works just like || operator (Logical OR) but it short circuits on non-nullish values. Let that sink in because that’s what it is designed to do.
⊙ i = undefined, j = undefined;
▶ i ?? j ?? 1
◁ 1
⊙ j = 0
▶ i ?? j ?? 1
◁ 0
In the above example, i ?? j ?? 1 returns 1 because both i and j are nullish since their values are undefined. Therefore undefined ?? undefined ?? 1 returns 1. However, after setting j=0, j is no longer nullish, therefore i ?? j ?? 1 returns 0 since undefined ?? 0 ?? 1 short circuits at j and value of j is returned.
So if try the earlier object properties example, we will now get satisfactory results. Let’s retry that again.
▶ obj?.a?.b?.c ?? "default"
◁ {p: 1}
▶ obj?.a?.b?.c?.p ?? "default"
◁ 1
▶ obj?.a?.b?.f ?? "default"
◁ "default"
▶ obj?.a?.b?.f?.p ?? "default"
◁ "default"
▶ obj?.a?.b?.d ?? "default"
◁ ""
▶ obj?.a?.b?.d?.p ?? "default"
◁ "default"
▶ obj?.a?.b?.e ?? "default"
◁ 0
So the difference in results of the above program from the earlier one where || operator was used is with the expression obj?.a?.b?.d ?? “default” which returns "" since the value of obj?.a?.b?.d is "" (non-nullish) and with the obj?.a?.b?.e ?? “default” which returns 0 since the value of obj?.a?.b?.e is 0 which again is non-nullish.
🐞 [proposal] [support] [babel-plugin]
Logical Assignment Operators
JavaScript is loaded with a ton of operators some of which are arithmetic assignment operators such as +=, -=, *=, /= and %= among many. Let’s see some examples of these operators in action.
💡 BTW, we are getting new ** and **= exponent operators in JavaScript just like in Python. This proposal has reached stage 3.
⊙ var i = 2;
▶ i -= 1 // i = 2 - 1 (i = i - 1)
◁ 1
▶ i += 2 // i = 1 + 2 (i = i + 1)
◁ 3
▶ i *= 2 // i = 3 * 2 (i = i * 1)
◁ 6
▶ i /= 2 // i = 6 / 2 (i = i / 1)
◁ 3
▶ i %= 2 // i = 3 % 2 (i = i % 1)
◁ 1
The operator i -= 1 expands to i = i — 1 at runtime as shown in the comments. Focus on one more thing here. In JavaScript, assignment expression returns the assigned value. Hence the expression i -= 1 returns 1 because the value assigned to i was i — 1 which is 1.
💡 JavaScript also has the comma operator. It is just like || except it doesn’t short circult and returns the value of the last expression. For example, the expression (0, 1, false, 3) returns 3. Hence var x = (0, 1, false, 3) assigns 3 to x. Read more about this on MDN.
We have these arithmetic assignment operators in JavaScript since the stone age and so does the Logical OR (||) and Logical AND (&&) operators. But we didn’t have logical assignment operators such as Logical OR assignment (||=) and Logical AND assignment (&&=) operators until now.
The Logical OR assignment operator ||= might seem just like += but it expands in a different fashion and so does the Logical AND assignment (&&=) operator. Let’s see how.
▶ i += j -> i = i + j
▶ i ||= j -> i || (i = j)
▶ i &&= j -> i && (i = j)
As you can see with the += operator, it expands into an assignment expression where the variable i will always be assigned with some value and the same goes for all arithmetic assignment operators.
However, that’s not the case with the expression i ||= j. Here the assignment expression expands to i || (i = j) which only assign value to i if the initial value of i is falsy. If i is falsy, (i = j) will be executed and this expression returns the value assigned to i which is j, therefore the entire expression i || (i = j) returns the value assigned to i. If i is truthy, this expression returns the value of i.
⊙ var i = undefined;
▶ i ||= 0 // i || (i = 0) -> undefined || (i = 0)
◁ 0 (assignment success)
▶ i ||= 1 // i || (i = 1) -> 0 || (i = 1)
◁ 1 (assignment success)
▶ i ||= 2 // i || (i = 2) -> 1 || (i = 2)
◁ 1 (assignment failed)
In the above example, the last assignment fails because at that point the value of i is truthy where the || operator short circuits.
The same logic can be applied to the Logical AND assignment (&&=) operator. In this case, the assignment will only work when the initial value of the variable is truthy. Let’s see an example.
⊙ var i = undefined;
▶ i &&= 0 // i && (i = 0) -> undefined && (i = 0)
◁ undefined (assignment failed)
⊙ i = 0
▶ i &&= 1 // i && (i = 1) -> 0 && (i = 1)
◁ 0 (assignment failed)
⊙ i = 1
▶ i &&= 2 // i && (i = 2) -> 1 && (i = 2)
◁ 2 (assignment success)
In the above example, only the last assignment expression worked since the value of i at that point is truthy and the && operator doesn’t short circuit.
Well, you could ask, what if I want to assign a value to a variable only when that variable is null or undefined. This is a valid argument because technically 0, false, "" and NaN is a valid value. So this proposal also brought us the Logical Nullish assignment (??=) operator.
⊙ var i = undefined;
▶ i ??= 0 // i ?? (i = 0) -> undefined ?? (i = 0)
◁ 0 (assignment success)
▶ i ??= 1 // i ?? (i = 1) -> 0 ?? (i = 1)
◁ 0 (assignment failed)
▶ i ??= 2 // i ?? (i = 2) -> 0 ?? (i = 2)
◁ 0 (assignment failed)
In the above case, the expression i ??= 0 resulted in the assignment of 0 to i because at that point, i was undefined which is a nullish value and ?? operator doesn’t short circuit. However, after that, the value of i is 0 which is non-nullish which means i ?? expr will result in i always. Hence the assignment expression i ??= 1 and i ??= 2 did not succeed as the value of i is 0 (non-nullish) and ?? operator short circuited.
🐞 [proposal] [support] [babel-plugin]
String.prototype.replaceAll
We all have used .replace() prototype function on a string value to replace some characters in the string with another string using a string match or a regular expression pattern. Let me show you how.
⊙ var str = "Hello World! This World seems nice.";
▶ str.replace( "World", "Universe" )
◁ "Hello Universe! This World seems nice."
The String.prototype.replace method does not mutate the string data (as strings are immutable) but returns a new string by replacing the match with the replacement. Here the match is "World" which is a string and replacement is "Universe" which also is a string.
💡 The String.prototype.replace method provides far more functionalities than just replacing a string with another string, such as substituting the matched string (target) in the replacement string using $& placeholder. Check the MDN documentation to know more about this.
However, look at the returned string value of the .replace() call in the above example. It only replaced one "World" word with the "Universe". Is that a mistake? Actually, not. The .replace() method only replace the first occurrence of the target when the target is a string.
To replace multiple occurrences, we need to use a Regular Expression object with the global flag g turned on. Let’s modify the earlier example.
⊙ var str = "Hello World! This World seems nice.";
▶ str.replace( /World/g, "Universe" )
◁ "Hello Universe! This Universe seems nice."
Well, now we have replaced all the occurrences of the “World” matched by the /World/g pattern with the "Universe". However, we had to use a regular expression to do that which is a little inconvenient.
Hence we now have String.prototype.replaceAll prototype method natively in JavaScript. This method work just like .replace() but it will replace all the occurrences of the target instead of just the first one.
⊙ var str = "Hello World! This World seems nice.";
▶ str.replaceAll( "World", "Universe" )
◁ "Hello Universe! This Universe seems nice."
So we didn’t have to use a regular expression to replace all the occurrences of the word "World" in the string which is super convenient. But we can also use the regular expression with .replaceAll just like .replace however with one caveat. Since .replaceAll supposed to replace all occurrences, a regular expression without g flags throws a TypeError exception.
⊙ var str = "Hello World! This World seems nice.";
▶ str.replace( "World", "Universe" )
◁ "Hello Universe! This World seems nice."
▶ str.replace( /World/, "Universe" )
◁ "Hello Universe! This World seems nice."
▶ str.replace( /World/g, "Universe" )
◁ "Hello Universe! This Universe seems nice."
▶ str.replaceAll( "World", "Universe" )
◁ "Hello Universe! This Universe seems nice."
▶ str.replaceAll( /World/g, "Universe" )
◁ "Hello Universe! This Universe seems nice."
▶ str.replaceAll( /World/, "Universe" )
◁ Uncaught TypeError: String.prototype.replaceAll called with a non-global RegExp argument
🐞 [proposal] [support] [polyfill]
Promise.any
In 2019, we received Promise.allSettled static method that returns a promise which resolves when all promises are settled (either resolved or rejected). We talked about this in the earlier lesson. This year, we have received Promise.any static method. This method is similar to Promise.race method with some exceptions.
The Promise.race is racing input promise until one gives a result. The result could be the resolution or rejection of the promise. Hence once one of the input promises settles, the promise returned by the Promise.race() resolves or rejects with the value of the first settled promise.
⊙ const delayed = ( id, success, time = 0 ) => {
return new Promise( ( resolve, reject ) => {
setTimeout( () => {
success ? resolve( `🟢 ${id}` ) : reject( `🔴 ${id}` )
}, time );
} );
}
▶ Promise.race( [
delayed( 'A', true, 1000 ),
delayed( 'B', true, 500 ),
delayed( 'C', false, 1500 ),
] )
.then( (result) => console.log('success ->', result) )
.catch( (result) => console.log('failure ->', result) );
◁ success -> 🟢 B
In the above example, the delayed function returns a promise that either resolves or rejects based on the success argument after some time based on the time argument. So the result of Promise.race in the above is a promise that resolves when one of the input promises A, B or C either resolves first or rejects when one of the input promises rejects first.
In the above example, the returned promise resolves with the resolved value of B since it resolves first among others. If it rejects then we would get the promise rejection with the rejected values as shown below.
▶ Promise.race( [
delayed( 'A', true, 1000 ),
delayed( 'B', false, 500 ),
delayed( 'C', false, 1500 ),
] )
.then( (result) => console.log('success ->', result) )
.catch( (result) => console.log('failure ->', result) );
◁ failure -> 🔴 B
This is working as expected but sometimes we need the value of the first resolved promise, not the value of the first settled promise. For example, if you want to make sure that the user always gets a network response, then you would call three different API endpoints to get the same response and serve the response of the first API that returned a success status (just an example).
This is where Promise.any comes in. This method works in the exact same manner as Promise.race but the promise returned by this method resolves when the first input promise successfully resolved with its resolved value.
When none of the input promises resolves (when all are rejected), this method throws an error of the type AggregateError. This error will be caught by the catch method or the catch block if you are using the try/catch with the async/await syntax.
▶ Promise.any( [
delayed( 'A', true, 1000 ),
delayed( 'B', false, 500 ),
delayed( 'C', true, 1500 ),
] )
.then( (result) => console.log('success ->', result) )
.catch( (result) => console.log('failure ->', result) );
◁ success -> 🟢 A
In the above example, though the promise B settles first but since it is rejected, Promise.any kept on waiting until other pending promises resolve. Since the promise A a successfully resolves after 1s, it will resolve immediately (after 1s) with the value of the promise A.
However, when all promises are rejected, the AggregateError error is thrown which contains the reason for the rejection.
▶ Promise.any( [
delayed( 'A', false, 1000 ),
delayed( 'B', false, 500 ),
delayed( 'C', false, 1500 ),
] )
.then( (result) => console.log('success ->', result) )
.catch( (result) => console.log('failure ->', result) );
◁ failure -> AggregateError: All promises were rejected
🐞 [proposal] [support] [polyfill]
If you want to know how promises work in TypeScript, here is the lesson.
A quick introduction to “Promises” and “Async/Await” (with new features)


What’s new in JavaScript 2020? was originally published in JsPoint on Medium, where people are continuing the conversation by highlighting and responding to this story.