JavaScript ES2021: New Features You Need to Know
JavaScript ES2021: New Features You Need to Know
ES2021 (ECMAScript 2021) brings several powerful new features to JavaScript. Let's explore each one with practical examples.
1. Logical Assignment Operators
Three new operators combine logical operations with assignment:
||= - Or Assignment
let a = null;
a ||= 'default';
console.log(a); // 'default'
// Only assigns if current value is falsy
let b = 'existing';
b ||= 'default';
console.log(b); // 'existing'
// Practical use: setting default values
const options = {};
options.timeout ??= 3000; // Sets default timeout
&&= - And Assignment
let a = 'value';
a &&= 'updated';
console.log(a); // 'updated'
let b = null;
b &&= 'updated';
console.log(b); // null (not updated)
// Practical use: conditional updates
const user = { name: 'John', active: true };
user.active &&= user.lastLogin > Date.now() - 86400000;
??= - Nullish Coalescing Assignment
let a = null;
a ??= 'default';
console.log(a); // 'default'
let b = '';
b ??= 'default';
console.log(b); // '' (empty string is not nullish)
// Practical use: optional configuration
const config = {
retries: 3
};
config.timeout ??= 5000;
config.retries ??= 1; // Won't override existing value
2. String.prototype.replaceAll()
No more regex workarounds for replacing all occurrences:
const text = 'hello world, hello universe';
// Old way with regex
const result1 = text.replace(/hello/g, 'hi');
// New way
const result2 = text.replaceAll('hello', 'hi');
console.log(result2); // 'hi world, hi universe'
// Practical example: sanitizing input
const userInput = '<script>alert("xss")</script>';
const sanitized = userInput
.replaceAll('<', '<')
.replaceAll('>', '>');
3. Promise.any()
Returns the first fulfilled promise:
const promise1 = Promise.reject('Error 1');
const promise2 = Promise.resolve('Success 2');
const promise3 = Promise.resolve('Success 3');
Promise.any([promise1, promise2, promise3])
.then(result => console.log(result)) // 'Success 2'
.catch(error => console.error(error));
// When all promises reject
Promise.any([promise1, Promise.reject('Error 2')])
.catch(error => {
console.log(error instanceof AggregateError); // true
console.log(error.errors); // ['Error 1', 'Error 2']
});
Practical Example: Fastest Server
async function getFastestServer() {
const servers = [
fetch('https://server1.example.com/api'),
fetch('https://server2.example.com/api'),
fetch('https://server3.example.com/api'),
];
try {
const response = await Promise.any(servers);
return response;
} catch (error) {
console.error('All servers failed:', error.errors);
}
}
4. AggregateError
New error type for representing multiple errors:
const errors = [
new Error('First error'),
new Error('Second error'),
new TypeError('Type error')
];
const aggregateError = new AggregateError(errors, 'Multiple errors occurred');
console.log(aggregateError.message); // 'Multiple errors occurred'
console.log(aggregateError.errors); // Array of errors
// Use case: Promise.any() failure
Promise.any([
Promise.reject(new Error('Failed 1')),
Promise.reject(new Error('Failed 2'))
]).catch(error => {
if (error instanceof AggregateError) {
error.errors.forEach(err => console.error(err.message));
}
});
5. Numeric Separators
Make large numbers more readable:
// Before
const budget = 1000000000;
const bytes = 1073741824;
// After
const budget = 1_000_000_000;
const bytes = 1_073_741_824;
// Various formats
const creditCard = 1234_5678_9012_3456;
const socialSecurity = 999_99_9999;
const hex = 0xFF_FF_FF_FF;
const binary = 0b1010_0101;
const octal = 0o1234_5670;
// Scientific notation
const million = 1e6;
const billion = 1_000e6;
6. WeakRef and FinalizationRegistry
WeakRef
Hold a reference without preventing garbage collection:
let obj = { data: 'important' };
const weakRef = new WeakRef(obj);
// Access the value
const value = weakRef.deref();
if (value) {
console.log(value.data); // 'important'
}
// The object can be garbage collected
obj = null;
// Later, it might be gone
const valueLater = weakRef.deref();
if (!valueLater) {
console.log('Object was garbage collected');
}
FinalizationRegistry
Run cleanup when objects are garbage collected:
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Cleaned up: ${heldValue}`);
});
function createResource() {
const resource = { data: new Array(1000000) };
registry.register(resource, 'resource-1');
return resource;
}
let res = createResource();
res = null; // Eventually triggers cleanup callback
Practical Use Case: Caching
class Cache {
#registry = new FinalizationRegistry(key => {
console.log(`Cleaning up cache for: ${key}`);
this.#cache.delete(key);
});
#cache = new Map();
set(key, value) {
const ref = new WeakRef(value);
this.#cache.set(key, ref);
this.#registry.register(value, key);
}
get(key) {
const ref = this.#cache.get(key);
return ref?.deref();
}
}
Browser Support and Transpilation
Most features are well-supported in modern browsers. For older environments:
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: '> 0.25%, not dead'
}]
]
};
Summary Table
| Feature | Description |
|---|---|
||= | Assign if falsy |
&&= | Assign if truthy |
??= | Assign if nullish |
replaceAll() | Replace all occurrences |
Promise.any() | First fulfilled promise |
AggregateError | Multiple errors wrapper |
| Numeric separators | _ in numbers |
WeakRef | Weak reference |
FinalizationRegistry | Cleanup callbacks |
Conclusion
ES2021 provides practical utilities that simplify common patterns. Logical assignment operators reduce boilerplate, replaceAll makes string manipulation cleaner, and Promise.any offers a useful alternative to Promise.race. Start using these features today with Babel or in modern browsers!