ES2021 New Features: 5 Major Updates for JavaScript Developers
This article introduces the five major new features of ES2021, including numeric separators, String.prototype.replaceAll(), Promise.any, logical assignment operators, and WeakRefs, with detailed explanations and code examples.
This article introduces the five major new features of ES2021, including numeric separators, String.prototype.replaceAll(), Promise.any, logical assignment operators, and WeakRefs, with detailed explanations and code examples.
ES2021 is the ECMAScript version corresponding to 2021. ECMAScript is a language specification standardized by ECMA (European Computer Manufacturers Association), while JavaScript is a scripting language implemented based on this specification standard. As front-end developers, we deal with JavaScript every day, and ECMAScript continuously adds new features to JavaScript, so it is necessary to understand the latest specifications of this language.
From the release of ECMAScript 1.0 in July 1997 to now, ECMAScript has officially released 12 versions from ES6/ES2015 to ES12/ES2021. Below we mainly introduce the new features released in ES2021.
ES2021 has been officially approved and brings 5 new features, which are introduced below:
1. Numeric Separator
In daily life, large numbers are usually separated by commas every three digits to facilitate quick identification of numbers. For example: 1,000,000 and 109,436,871.2. This makes it easier to say that the first number is 1 million, and the second number is about 100 million.
In code, how do we do it? Defining a constant of 100,000,000, do we write it like this?
const count1 = 1000000000 // not intuitive
Obviously, for code readability, we often use the * operator to separate numbers. The above constant can be defined like this:
const count2 = 100 * 0000 * 000 // very intuitive
Similarly, if you want to write code that executes after 10 minutes, you will use the setTimeout() timer. For easy code reading, we often write it like this:
setTimeout(() => { // ... I will execute after 10 minutes console.log('10min is up') }, 10 * 1000 * 1000)
This is because the time parameter accepted by setTimeout() is in milliseconds. Obviously, compared to writing 10000000 directly, writing 10 * 1000 * 1000 will increase the calculation process.
So is there a way to make numbers in code easier to read without using operators?
In ES2021, a new feature of numeric separator _ is added to meet the above requirements. The above numbers can be written as:
1_000_000 // 1,000,000
109_436_871.2 // 109,436,871.2
const count3 = 1_000_000_000 // 1000000000
console.log(count3)
setTimeout(() => { // ... I will execute after 10 minutes console.log('10min is up') }, 10_1000_1000) // 10 * 1000 * 1000
Will the writing method using separators change this number? You can print the results of count1, count3, and count1 === count3 to see:
console.log(count1) // 1000000000
console.log(count3) // 1000000000
console.log(count1 === count3) // true
Separators are not only applicable to decimal, but also support binary, hexadecimal, and BigInts introduced in ES2020.
But it is worth noting that:
1. The _ cannot be used at the end of a number, for example:
const count4 = 1_000_000_000_
console.log(count4)
2. The _ cannot be used at the beginning of a number, for example:
const count5 = _1_000_000_000
console.log(count5)
3. The _ cannot be used consecutively, for example:
const count6 = 1__000_000_000
console.log(count6)
When using new features, we need to consider the support of mainstream browsers for it. Currently, the support of numeric separators by various browsers is as follows:
2. String.prototype.replaceAll()
String.prototype.replaceAll() is another new feature introduced in ES2021, mainly responsible for global string replacement. Before introducing String.prototype.replaceAll(), let's review what existing string replacement methods are available? For example, how to replace js with JavaScript in the following string?
const str = 'js js 2021 js 2021 js js js'
Method 1: String.prototype.replace()
const str1 = str.replace('js', 'JavaScript')
console.log('str----replace', str1) // str----replace JavaScript js 2021 js2021 js js js
Method 2: Regular expression + String.prototype.replace()
const str2 = str.replace(/js/g, 'JavaScript')
console.log('str----replace + regular expression', str2) // str----replace + regular expressionJavaScript JavaScript 2021 JavaScript 2021 JavaScript JavaScript JavaScript
Method 3: split('').join('')
const str3 = str.split('js').join('JavaScript')
console.log('str----split', str3) // str----split JavaScript JavaScript2021 JavaScript 2021 JavaScript JavaScript JavaScript
It can be seen that String.prototype.replace() used with the string parameter js will only affect the first occurrence, and when used with the global regular expression /js/g, it will achieve the effect of global replacement. However, regular expressions are very unfriendly to novices and require a certain learning and hands-on cost. In addition to the regular expression + String.prototype.replace() method, split('').join('') can also achieve global replacement, but this way of splitting first and then combining will increase overhead and slow down operation.
To solve the above problems, ES2021 introduces the new feature String.prototype.replaceAll(), whose API design is consistent with String.prototype.replace(), that is, String.prototype.replaceAll(searchValue, replaceValue). The usage effect is equivalent to String.prototype.replace(searchValue, replaceValue), when replaceValue is a global regular expression.
const str4 = str.replaceAll('js', 'JavaScript')
console.log('str----replaceAll', str4) // str----replaceAll JavaScriptJavaScript 2021 JavaScript 2021 JavaScript JavaScript JavaScript
Currently, major browsers' support for String.prototype.replaceAll is as follows:
3. Promise.any
Since the introduction of Promise in ES2015, Promise.all, Promise.race, and Promise.allSettled combinators have been supported. In ES2021, Promise.any is newly introduced.
Promise.any accepts a Promise iterable object (such as an array):
let promises = [ Promise.reject('ERROR A'), Promise.reject('ERROR B'), Promise.resolve('result') ]
As long as one of the promises is fulfilled, it returns a resolved promise:
Promise.any(promises).then(value => console.log('value: ', value)) .catch(err => console.error('err: ', err))
Output result:
value: result
When all promises are rejected, it returns a rejected promise:
promises = [ Promise.reject('ERROR A'), Promise.reject('ERROR B'), Promise.reject('ERROR C') ]
Output result:
err: AggregateError: All promises were rejected
Compared with Promise.any, what are the differences between Promise.all and Promise.race?
1. Promise.all (ES2015): As long as one of the promises is rejected, it immediately returns a rejected promise; when all promises are fulfilled, it returns a resolved promise. The result of Promise.any is the opposite of Promise.all.
Promise.all([ Promise.reject('ERROR'), Promise.resolve('result') ]).then(value => console.log('value:', value)) .catch(err => console.error('err:', err))
Output result:
err: ERROR
2. Promise.race (ES2015): As long as a promise is fulfilled or rejected, it immediately returns a resolved or rejected promise. It mainly focuses on whether the Promise has been resolved, regardless of whether it is resolved or rejected.
Promise.race([ Promise.reject('ERROR'), Promise.resolve('result') ]).then(value => console.log('value:', value)) .catch(err => console.error('err:', err))
After understanding the difference between Promise.any and Promise.all, Promise.race, how can you implement Promise.any yourself?
Promise.any only needs to resolve the incoming promise as long as one of them is fulfilled, otherwise it will collect all the reject results and return AggregateError. So you can have:
const PromiseAny = (promises) => { return new Promise((resolve,reject)=>{ promises = Array.isArray(promises) ? promises : [] let len = promises.length // Used to collect all reject let errs = [] // If an empty array is passed in, it will directly return AggregateError if(len === 0) return reject(new AggregateError('All promises were rejected')) promises.forEach((promise)=>{ promise.then(value=>{ resolve(value) },err=>{ len-- errs.push(err) if(len === 0){ reject(new AggregateError(errs)) } }) }) }) }
Experiment:
PromiseAny(promises).then(value => console.log('value:', value)) .catch(err => console.error('err:', err))
Output result:
err: AggregateError
Currently, mainstream browsers' support for Promise.any is as follows:
4. Logical Assignment Operators
Logical assignment operators are composed of logical operators and assignment expressions:
1. ||=
a ||= b
Equivalent to a || (a = b)
Equivalent to if (!a) { a = b }
2. &&=
a &&= b
Equivalent to a && (a = b)
Equivalent to if (a) { a = b }
3. ??=
a ??= b
Equivalent to a ?? (a = b)
Equivalent to if (a === null || a === undefined) { a = b }
Try to guess the output of the following code?
let obj = {} obj.x ??= 0
console.log(obj.x) obj.x ||= 1
console.log(obj.x) obj.x &&= 2
console.log(obj.x)
Output result:
0
1
2
It is worth noting that:
Logical assignment operators will only assign values when the condition is met. Please think about:
a = a || b and a ||= b are equivalent??
a = a && b and a &&= b are equivalent??
a = a ?? b and a ??= b are equivalent??
Obviously, they are not equivalent. This is because for a = a || b, no matter what the result of a || b is, this result will be assigned to a; while for a ||= b, only when a is false, b will be assigned to a. The other two are similar.
Currently, mainstream browsers' support for various logical assignment operators is as follows:
5. WeakRefs
Weakref instances can be used as weak references to objects. Weak references to objects refer to the behavior of not preventing the GC from reclaiming when the object should be reclaimed by the GC.
Weakref instances have a method deref that returns the original object being referenced. If the original object has been collected, it returns undefined. A simple example of using Weakref:
const ref = new WeakRef({ name: 'js' })
let obj = ref.deref()
if (obj) { console.log(obj.name) }
Note: It is best to avoid using
Currently, mainstream browsers' support for WeakRefs is as follows:
III. How to Use ES2021 Features in Code Library
The latest versions of mainstream browsers (such as Chrome85+, Firefox 79+, Safari14) already support the new features of ES2021. If you want to use them in older versions of browsers, you can use the Babel tool.
Currently, Babel official (https://babeljs.io/docs/en/plugins-list#es2021) has provided plugins for logical assignment operators and numeric separators. Support for other features still needs to wait patiently.
IV. Summary
1. String.prototype.replaceAll: With this API, there is no need to write regular expressions to replace characters
2. Promise.any: Returns the first fulfilled promise, if all are rejected, it returns an AggregateError with the failure reason
3. New logical assignment operators added: ??=, &&=, ||=
4. WeakRefs: Use weak reference objects, which will not prevent GC, and can use WeakRef.prototype.deref() to release the reference before GC
5. Underscore _ separator: Use _ to separate numbers for easy reading
V. References
1. https://tc39.es/ecma262/#sec-string.prototype.replaceall
2. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll
3. https://juejin.cn/post/6969016993215152136
4. https://juejin.cn/post/6923026786825699341#heading-4
5. https://github.com/tc39
6. https://juejin.cn/post/6977431269210587166
7. https://babeljs.io/docs/en/babel-plugin-proposal-numeric-separator
8. https://babeljs.io/docs/en/babel-plugin-proposal-numeric-separator
Beike Product & Technology
As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.