Overview Of ECMAScript 2021 – ES2021 – “ES12” Features
Hello, JavaScript enthusiasts!
ECMAScript 2021, also known as ES12, introduced several new features and improvements to JavaScript. Here’s a summary of the key updates:
Some of the key features introduced in ES8 include:
- Logical Assignment Operators
- Numeric Separators
- String.prototype.replaceAll()
- Promise.any()
- WeakRefs
- FinalizationRegistry
- WeakRefs and FinalizationRegistry
- Import.meta.url
- Logical Operators with Assignment
1. Logical AND Assignment (&&=
)
In ES12 (ECMAScript 2021), logical assignment operators were introduced as a way to simplify and condense common logical operations in JavaScript. These operators combine logical operations with assignments, making the code more concise. Here’s a rundown of how they work:
Logical AND Assignment (&&=
)
a &&= b;
This operator works as follows:
- If
a
is truthy, thena
is assigned the value ofb
. - If
a
is falsy, thena
remains unchanged.
let a = 5;
let b = 10;
a &&= b; // Since a (5) is truthy, a is now 10.
console.log(a); // Output: 10
a = 0;
a &&= b; // Since a (0) is falsy, a remains 0.
console.log(a); // Output: 0
Logical OR Assignment (||=
)
a ||= b;
This operator works as follows:
- If
a
is falsy, thena
is assigned the value ofb
. - If
a
is truthy, thena
remains unchanged.
let a = null;
let b = 10;
a ||= b; // Since a (null) is falsy, a is now 10.
console.log(a); // Output: 10
a = 5;
a ||= b; // Since a (5) is truthy, a remains 5.
console.log(a); // Output: 5
Logical Nullish Assignment (??=
)
a ??= b;
This operator works as follows:
- If
a
isnull
orundefined
, thena
is assigned the value ofb
. - If
a
has any other value (including falsy values like0
,NaN
, or''
), thena
remains unchanged.
let a = null;
let b = 10;
a ??= b; // Since a (null) is nullish, a is now 10.
console.log(a); // Output: 10
a = 0;
a ??= b; // Since a (0) is not nullish, a remains 0.
console.log(a); // Output: 0
These operators help reduce boilerplate code and make logical operations with assignments more intuitive and expressive.
2. Numeric Separators
In ES12 (ECMAScript 2021), numeric separators were introduced to improve the readability of numeric literals in JavaScript. This feature allows you to use underscores (_
) to separate groups of digits in numeric literals, making large numbers easier to read and understand.
Here’s a brief overview of how numeric separators work:
Basic Usage
You can place underscores between digits in numeric literals to visually separate groups of digits. This can be particularly useful for large numbers:
const million = 1_000_000; // Readable representation of 1,000,000
const binary = 0b1010_1011_1100_1111; // Binary literal with separators
const hex = 0xFF_EC_DE_5E; // Hexadecimal literal with separators
const float = 1_234_567.89; // Floating-point number with separators
Placement Rules
- In Integers: You can use underscores between digits in integer literals. You cannot place them at the beginning, end, or next to the decimal point. For example,
1_000_000
is valid, but_1_000_000
is not. - In Floating Points: You can use underscores to separate digits before and after the decimal point. For example,
1_234_567.89
is valid, but1_234_567._89
is not. - In Binary, Octal, and Hexadecimal Literals: You can use underscores in binary (
0b
), octal (0o
), and hexadecimal (0x
) literals. For example,0b1010_1011
and0xFF_EC_DE
are valid.
Invalid Uses
- Leading and Trailing: You cannot have underscores at the beginning or end of the number, e.g.,
_1_000
or1_000_
. - Adjacent to Decimal Point: You cannot place underscores adjacent to the decimal point, e.g.,
1_000._000
is invalid. - Multiple Consecutive Underscores: Consecutive underscores are not allowed, e.g.,
1__000
is invalid.
Here are some more examples to illustrate:
const largeNumber = 123_456_789; // 123,456,789
const hexValue = 0xA1_B2_C3_D4; // 2692546148
const binaryValue = 0b1101_0011; // 211 (in decimal)
const floatValue = 3.14_15_92; // 3.141592
const bigIntValue = 1_000_000_000n; // BigInt value: 1000000000
Summary
Numeric separators make it easier to read and work with large numbers by allowing you to group digits logically. They help in maintaining code readability and reduce the likelihood of errors when dealing with numeric values in JavaScript.
3. String.prototype.replaceAll()
In ES12 (ECMAScript 2021), the String.prototype.replaceAll()
method was introduced to simplify the process of replacing all occurrences of a substring or pattern in a string. Prior to this method, replacing all occurrences of a substring required using a regular expression with the global flag (g
), which could be cumbersome and less intuitive.
The replaceAll()
method creates a new string with all occurrences of a specified substring or pattern replaced by a new substring. This method is simpler and more readable than using regular expressions for this purpose.
Replacing All Occurrences of a Substring
const text = "Hello world! Welcome to the world!";
const newText = text.replaceAll("world", "universe");
console.log(newText); // Output: "Hello universe! Welcome to the universe!"
Replacing All Occurrences Using a Regular Expression
const text = "Hello world! Hello universe!";
const newText = text.replaceAll(/hello/i, "Hi");
console.log(newText); // Output: "Hi world! Hi universe!"
In this case, the regular expression /hello/i
matches “hello” regardless of case, and the global flag (g
) ensures all occurrences are replaced.
Important Notes
- replaceAll() vs replace(): The replace() method only replaces the first occurrence or a specified occurrence of the substring. To replace all instances, you would typically need to use a regular expression with the global flag. replaceAll() simplifies this by handling all replacements directly.
const text = "foo bar foo"; const replacedWithReplace = text.replace(/foo/, "baz"); // Only replaces the first occurrence const replacedWithReplaceAll = text.replaceAll("foo", "baz"); // Replaces all occurrences console.log(replacedWithReplace); // Output: "baz bar foo" console.log(replacedWithReplaceAll); // Output: "baz bar baz"
- Immutable Strings: Strings in JavaScript are immutable, so
replaceAll()
returns a new string with the replacements, leaving the original string unchanged. - Error Handling: If the
searchValue
is a regular expression that does not have the global flag, it will throw aTypeError
.const text = "foo foo foo"; try { text.replaceAll(/foo/, "bar"); // Error: "searchValue" must be global RegExp or string } catch (e) { console.error(e); // Output: TypeError: replaceAll() requires a global RegExp or a string }
Overall, String.prototype.replaceAll()
enhances the readability and convenience of string manipulation in JavaScript, especially when you need to perform multiple replacements.
4. Promise.any()
In ES12 (ECMAScript 2021), the Promise.any()
method was introduced to handle cases where you need to wait for the first promise to fulfill successfully among a set of promises. This method is useful when you want to proceed with the result of the first resolved promise and ignore the rest, even if some of the promises are rejected.
Overview
Promise.any()
takes an iterable of Promise objects and, as soon as one of the promises fulfills, returns a single Promise that resolves with the value from that promise. If all of the given promises are rejected, Promise.any()
returns a promise that rejects with an AggregateError
— a new subclass of Error
that groups together all the individual errors.
Basic Example
const p1 = Promise.reject('Error 1');
const p2 = Promise.resolve('Success 2');
const p3 = Promise.reject('Error 3');
Promise.any([p1, p2, p3])
.then(result => {
console.log(result); // Output: "Success 2"
})
.catch(error => {
console.error(error);
});
In this example, p2
is the first promise to fulfill successfully, so Promise.any()
resolves with the value 'Success 2'
.
Handling All Promises Being Rejected
If all the promises are rejected, Promise.any()
will reject with an AggregateError
, which contains an array of all the rejection reasons.
const p1 = Promise.reject('Error 1');
const p2 = Promise.reject('Error 2');
const p3 = Promise.reject('Error 3');
Promise.any([p1, p2, p3])
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error); // Output: AggregateError: All promises were rejected
console.error(error.errors); // Output: ["Error 1", "Error 2", "Error 3"]
});
In this case, since all the promises are rejected, Promise.any()
returns an AggregateError
with all the individual errors.
Key Points
- Fulfillment vs. Rejection:
Promise.any()
only resolves when the first promise fulfills. It ignores rejected promises until one of the promises resolves successfully. If all promises are rejected, it handles this scenario by rejecting withAggregateError
. - AggregateError: This is a special type of error introduced in ES12 to represent multiple errors. It is used by
Promise.any()
to aggregate all rejection reasons. - Iterables: The
iterable
passed toPromise.any()
can be any iterable object, like an array. However, all elements in the iterable must be promises or values that can be converted to promises. - Order of Fulfillment: If multiple promises fulfill at the same time,
Promise.any()
resolves with the result of the first promise in the iterable that fulfills.
Summary
Promise.any()
provides a way to handle scenarios where you are interested in the first successful result from a set of promises, rather than waiting for all of them to settle or handling errors immediately. It simplifies handling multiple asynchronous operations where you want to proceed as soon as one operation succeeds.
5. WeakRefs
In ES12 (ECMAScript 2021), the WeakRef
class was introduced as part of the ECMAScript specification to provide a way to hold weak references to objects. Weak references allow you to hold a reference to an object without preventing that object from being garbage-collected. This can be useful in scenarios like caching, where you want to hold on to objects while they are still needed but allow them to be cleaned up when they are no longer in use elsewhere.
Overview of WeakRef
- Purpose:
WeakRef
provides a way to reference an object without preventing it from being garbage-collected. This is particularly useful for scenarios where you want to maintain a reference to an object that may be freed if there are no other references to it. - Behavior: A
WeakRef
does not prevent its target object from being collected by the garbage collector. If the target object is collected, accessing it through aWeakRef
will result in anull
orundefined
reference.
Methods
deref()
: This method returns the target object if it is still reachable. If the object has been garbage-collected, deref()
returns undefined
.
Example Usage
Here’s a basic example demonstrating the use of WeakRef
:
let obj = { name: 'Example Object' };
let weakRef = new WeakRef(obj);
// Accessing the object through WeakRef
console.log(weakRef.deref()); // Output: { name: 'Example Object' }
// Dropping the strong reference
obj = null;
// Attempt to access the object again
console.log(weakRef.deref()); // Output: undefined (object might have been collected)
Use Cases
- Caching:
WeakRef
can be used in caching mechanisms where you want to cache objects but allow them to be garbage-collected when memory is low. This way, you don’t need to manually manage the lifecycle of the cached objects. - Memoization: In scenarios where you want to memoize results of expensive computations but allow those results to be cleaned up if they are no longer needed,
WeakRef
can be helpful.
Important Considerations
- Garbage Collection: Objects referenced by
WeakRef
are not guaranteed to remain alive. If the object is collected,WeakRef.deref()
will returnundefined
. Thus, you should always handle the case where the object might have been garbage-collected. - Usage:
WeakRef
should be used cautiously as it can make code harder to reason about, especially in complex scenarios where the garbage collection behavior affects program logic. - Not for Direct Manipulation: Unlike
WeakMap
andWeakSet
,WeakRef
is not typically used for directly managing weak references but rather for more specialized cases where weak references are needed.
Summary
WeakRef
provides a way to hold references to objects without preventing them from being garbage-collected. This feature is useful in certain scenarios like caching and memoization, where you want to manage object lifetimes without preventing garbage collection. It’s important to handle cases where the object may have been collected and to understand that WeakRef
does not guarantee the object's persistence.
6. FinalizationRegistry
In ES12 (ECMAScript 2021), the FinalizationRegistry
class was introduced to provide a way to perform cleanup operations when objects are garbage-collected. This is useful for managing resources or performing finalization tasks for objects that may be cleaned up by the JavaScript engine.
Overview of FinalizationRegistry
Purpose: FinalizationRegistry
allows you to register a callback to be invoked when objects are garbage-collected. This can be helpful for cleaning up resources or performing any final operations related to the objects that are no longer needed.
Behavior: When an object registered with a FinalizationRegistry
is garbage-collected, the callback associated with that object is invoked, allowing you to clean up or perform necessary operations related to the object.
Registering an Object
registry.register(object, heldValue, unregisterToken);
object
: The object that you want to register with theFinalizationRegistry
. This is the object you want to track for garbage collection.heldValue
: An optional value that can be associated with the object. This value is passed to thecleanupCallback
when the object is collected.unregisterToken
: An optional token that can be used to unregister the object before it is garbage-collected.
Unregistering an Object
registry.unregister(unregisterToken);
unregisterToken
: The token used to unregister the object. This prevents the cleanupCallback
from being called for that object.
Example Usage
Here's an example demonstrating how FinalizationRegistry
works:
// Define the cleanup callback
function cleanupCallback(heldValue) {
console.log(`Cleaning up: ${heldValue}`);
}
// Create a FinalizationRegistry instance
const registry = new FinalizationRegistry(cleanupCallback);
// Register an object with the registry
let obj = { name: 'Example Object' };
const token = {}; // Unregister token
registry.register(obj, 'Object was here', token);
// At this point, `obj` still exists, so the cleanup callback has not been called yet
console.log('Object registered');
// Remove strong reference to the object
obj = null;
// Force garbage collection (in some environments, you might need to manually trigger this)
// The cleanup callback should be called when `obj` is collected
Key Points
- Cleanup Callback: The
cleanupCallback
function is invoked with theheldValue
when the registered object is garbage-collected. This allows you to perform cleanup operations related to the object. - Unregistering: You can unregister objects from the
FinalizationRegistry
using an optional token. This stops the cleanup callback from being invoked for that specific object. - Garbage Collection Timing: The exact timing of when the
cleanupCallback
is called is not guaranteed and depends on when the JavaScript engine decides to perform garbage collection. This means you should not rely on precise timing for cleanup operations. - Usage Scenarios:
FinalizationRegistry
is particularly useful for resource management in cases where you need to release external resources or perform cleanup operations when objects are no longer in use. - Object Identity: The registry tracks objects by their identity, not by their value. Therefore, if the object is replaced with another object that has the same value but a different reference, the
FinalizationRegistry
will not treat them as the same object.
Summary
FinalizationRegistry
provides a mechanism for handling cleanup tasks related to objects that are about to be garbage-collected. It allows you to register objects and specify a callback to be invoked when those objects are no longer reachable. This is useful for managing resources, performing cleanup, and ensuring that finalization tasks are handled appropriately when objects are removed from memory.
7. WeakRefs and FinalizationRegistry
In ES12 (ECMAScript 2021), WeakRef
and FinalizationRegistry
were introduced as part of the standard to manage memory and cleanup resources more effectively. Both features facilitate handling objects and executing operations upon their garbage collection, but they serve distinct purposes and are suited to different scenarios.
WeakRef
WeakRef
provides a way to hold a weak reference to an object, which allows that object to be garbage-collected even if it is referenced by a WeakRef
. This can be useful for scenarios like caching, where you want to keep a reference to an object but allow it to be collected if no other strong references exist.
Key Features
- Weak Reference: A
WeakRef
does not prevent its target object from being collected by the garbage collector. - Deref Method: You can use the
deref()
method to access the target object if it is still reachable. If the object has been collected,deref()
returnsundefined
.
let obj = { name: 'Example Object' };
let weakRef = new WeakRef(obj);
// Access the object through WeakRef
console.log(weakRef.deref()); // Output: { name: 'Example Object' }
// Drop the strong reference
obj = null;
// Attempt to access the object again
console.log(weakRef.deref()); // Output: undefined (object might have been collected)
FinalizationRegistry
The FinalizationRegistry allows for the registration of cleanup callbacks that are triggered when objects are garbage-collected. This is useful for managing resource cleanup or finalization tasks when objects are no longer in use.
Key Features
- Cleanup Callback: Allows you to register a callback function that is invoked when the registered object is garbage-collected.
- Registration: You can register objects with a
FinalizationRegistry
and optionally associate a value with the object. - Unregistering: You can unregister objects using a token to prevent the callback from being invoked.
// Define the cleanup callback
function cleanupCallback(heldValue) {
console.log(`Cleaning up: ${heldValue}`);
}
// Create a FinalizationRegistry instance
const registry = new FinalizationRegistry(cleanupCallback);
// Register an object with the registry
let obj = { name: 'Example Object' };
const token = {}; // Unregister token
registry.register(obj, 'Object was here', token);
// At this point, `obj` still exists, so the cleanup callback has not been called yet
console.log('Object registered');
// Remove strong reference to the object
obj = null;
// Force garbage collection (in some environments, you might need to manually trigger this)
// The cleanup callback should be called when `obj` is collected
Using WeakRef
and FinalizationRegistry
Together
In some scenarios, you might use both WeakRef
and FinalizationRegistry
together. For example, you can use a WeakRef to maintain a weak reference to an object, while a FinalizationRegistry ensures that a cleanup callback is triggered when the object is garbage-collected.
// Define the cleanup callback
function cleanupCallback(heldValue) {
console.log(`Cleaning up: ${heldValue}`);
}
// Create a FinalizationRegistry instance
const registry = new FinalizationRegistry(cleanupCallback);
// Create a WeakRef instance
let obj = { name: 'Example Object' };
const weakRef = new WeakRef(obj);
// Register the object with the FinalizationRegistry
registry.register(obj, 'Object was here');
// Remove strong reference
obj = null;
// Check if WeakRef still holds the object
console.log(weakRef.deref()); // Output: undefined (object might have been collected)
// The cleanupCallback will be invoked when the object is garbage-collected
Summary
WeakRef
: Provides a weak reference to an object, allowing the object to be garbage-collected if no strong references exist. UseWeakRef
to implement features like caches or to reference objects without preventing their collection.FinalizationRegistry
: Registers cleanup callbacks that are invoked when objects are garbage-collected. UseFinalizationRegistry
to handle resource cleanup or finalization tasks for objects when they are no longer reachable.
Both features are designed to manage memory and cleanup more effectively and should be used according to the specific needs of your application, ensuring efficient resource management and proper cleanup of unused objects.
8. Import.meta.url
In ES12 (ECMAScript 2021), the import.meta.url
feature was introduced as part of the ECMAScript modules (ESM) specification. This feature provides a way to access the URL of the module in which the import.meta
is used. It's useful for various purposes, such as resolving relative URLs or understanding the location of the module within the application.
Overview of import.meta.url
- Purpose:
import.meta.url
gives you access to the URL of the current module. This URL can be useful for resolving paths relative to the module or for debugging purposes. - Usage Context: This feature is available only in ECMAScript modules, which are supported in environments that comply with the ES module specification.
Basic Example
// In a file named `module.js`
console.log(import.meta.url);
// Output might be: "file:///path/to/your/module.js" (in a Node.js environment) or an absolute URL in a browser environment
Resolving Relative Paths
You can use import.meta.url
to resolve paths relative to the module. This is especially useful in environments like Node.js or when working with module bundlers.
// In a file named `module.js`
import path from 'path'; // Node.js module for handling file paths
const currentUrl = new URL(import.meta.url);
const resolvedPath = path.resolve(currentUrl.pathname, '../data/file.txt');
console.log(resolvedPath);
// Output might be: "/path/to/your/data/file.txt"
In this example, new URL(import.meta.url)
converts the module URL to a URL
object, and path.resolve
is used to compute a file path relative to the module.
Use Cases
- Dynamic Imports:
import.meta.url
is useful for dynamically importing modules or resources based on the module's location. For example, you might use it to construct paths to other modules or assets in a way that is relative to the current module. - Relative Resource Loading: When loading resources or assets relative to a module,
import.meta.url
provides a reliable way to construct URLs or file paths that work consistently across different environments. - Debugging and Diagnostics: Knowing the URL of the module can be helpful for debugging and diagnostics, as it provides context about where the module is located.y
Important Considerations
- Browser vs. Node.js: The format of
import.meta.url
differs depending on the environment. In browsers, it is usually a URL that includes the protocol (http://
,https://
, etc.), whereas in Node.js, it is a file URL. - Module Resolution: When working with tools or bundlers, be aware that
import.meta.url
behavior might be influenced by how the tool resolves modules and paths. - Not Available in Scripts:
import.meta.url
is only available in ES modules. It is not available in traditional script contexts or CommonJS modules.
Summary
import.meta.url
provides the URL of the module in which it is used. This feature is particularly useful for resolving paths relative to the module or for managing dynamic imports. It enhances the ability to work with modules in a more context-aware manner, making it easier to handle resources and debug issues in both browser and Node.js environments.
9. Logical Operators with Assignment
In ES12 (ECMAScript 2021), the logical assignment operators were introduced to simplify and condense common logical operations involving assignment. These operators combine logical operations (&&
, ||
, ??
) with assignment into a single, more concise syntax.
Logical Assignment Operators
1. Logical AND Assignment (&&=
)
The &&=
operator assigns a value to a variable only if the variable is truthy. It is a shorthand for combining a logical AND operation with assignment.
let a = 5;
let b = 10;
a &&= b; // Since a (5) is truthy, a is now 10.
console.log(a); // Output: 10
a = 0;
a &&= b; // Since a (0) is falsy, a remains 0.
console.log(a); // Output: 0
Logical OR Assignment (||=
)
The ||=
operator assigns a value to a variable only if the variable is falsy. It is a shorthand for combining a logical OR operation with assignment.
let a = null;
let b = 10;
a ||= b; // Since a (null) is falsy, a is now 10.
console.log(a); // Output: 10
a = 5;
a ||= b; // Since a (5) is truthy, a remains 5.
console.log(a); // Output: 5
Logical Nullish Assignment (??=
)
The ??=
operator assigns a value to a variable only if the variable is null
or undefined
. It is a shorthand for combining a logical nullish coalescing operation with assignment.
let a = null;
let b = 10;
a ??= b; // Since a (null) is nullish, a is now 10.
console.log(a); // Output: 10
a = 0;
a ??= b; // Since a (0) is not nullish, a remains 0.
console.log(a); // Output: 0
Key Points
- Logical AND Assignment (
&&=
): Useful for conditionally updating a variable only if it is already truthy. - Logical OR Assignment (
||=
): Useful for setting a variable to a default value only if it is falsy. - Logical Nullish Assignment (
??=
): Useful for setting a variable to a default value only if it isnull
orundefined
.
These logical assignment operators help streamline code by reducing boilerplate and making logical operations involving assignment more readable and concise. They are especially helpful in scenarios where you frequently need to update variables based on their current value and a logical condition.
Happy coding!
If you missed my recent posts on ES6 (ECMAScript 2015), ES7 (ECMAScript 2016), ES8 (ECMAScript 2017), ES9 (ECMAScript 2018), ES10 (ECMAScript 2019) and ES11 (ECMAScript 2020), you can catch up by clicking the links below to explore their features and updates.
Click here to explore ES6 (ECMAScript 2015)
Click here to explore ES7 (ECMAScript 2016)
Click here to explore ES8 (ECMAScript 2017)
Click here to explore ES9 (ECMAScript 2018)
Click here to explore ES10 (ECMAScript 2019)
Click here to explore ES11 (ECMAScript 2020)
FAQs
How do Logical Assignment Operators work in ES2021?
Logical Assignment Operators are shorthand for combining logical operations with assignment. They streamline common patterns:
x &&= y
: Equivalent tox && (x = y)
.x ||= y
: Equivalent tox || (x = y)
.x ??= y
: Equivalent tox ?? (x = y)
.
These operators help in writing more concise code, especially when dealing with default values or conditions.
What is the purpose of Numeric Separators in ES2021?
Numeric Separators allow you to use underscores (_
) to separate digits in numeric literals, making large numbers easier to read. For example:
1_000_000
is easier to read than1000000
.0b1010_1011
for binary literals.0xFF_FF_FF_FF
for hexadecimal literals.
This feature helps developers quickly understand the magnitude of numbers, enhancing code readability.
Can you explain the new String.prototype.replaceAll() method?
The String.prototype.replaceAll()
method allows you to replace all occurrences of a substring or pattern within a string. Unlike replace()
, which only replaces the first occurrence or requires a global regular expression to replace multiple occurrences, replaceAll()
simplifies the process:
const str = 'hello world world';
const newStr = str.replaceAll('world', 'there');
console.log(newStr); // Output: 'hello there there'
This method improves efficiency and readability when replacing multiple substrings.
What does Promise.any() do in ES2021?
Promise.any()
takes an iterable of Promise objects and, as soon as one of the promises in the iterable fulfills, returns a single promise that resolves with the value from that promise. If no promises fulfill (all are rejected), it returns a promise that rejects with an AggregateError containing all rejection reasons.
const p1 = Promise.reject('Error 1');
const p2 = Promise.reject('Error 2');
const p3 = Promise.resolve('Success');
Promise.any([p1, p2, p3])
.then(value => console.log(value)) // Output: 'Success'
.catch(error => console.log(error));
What are WeakRefs and how are they used?
WeakRef
provides a way to hold a weak reference to an object, meaning that the object can be garbage-collected if there are no other strong references to it. This is useful for cases where you want to maintain a reference without preventing garbage collection:
let obj = { name: 'example' };
let weakRef = new WeakRef(obj);
obj = null; // Remove the strong reference
let deref = weakRef.deref();
console.log(deref); // Might be undefined if the object has been garbage collected
Weak references are used to avoid memory leaks by not preventing garbage collection of objects that are no longer needed.
One thought on “Overview Of ECMAScript 2021 – ES2021 – “ES12” Features”