String replaceAll() Method
Private Methods
Private Accessors
WeakRef and Finalizers
Finalizers
Promise.any() and AggregateError
Logical Assignment Operator
Logical assignment operator with
??
String replaceAll() Method
String.prototype.replaceAll()
replaces all occurrence of a string with another string value.
Currently JavaScript string has a replace()
method. It can be used to replace a string with another string.
const str = "Backbencher sits at the Back";
const newStr = str.replace("Back", "Front");
console.log(newStr); // "Frontbencher sits at the Back"
If the input pattern is a string, replace()
method only replaces the first occurrence. That is why in the code, the second occurrence of "Back"
is not replaced.
We can do a full replacement only if we supply the pattern as a regular expression.
const str = "Backbencher sits at the Back";
const newStr = str.replace(/Back/g, "Front");
console.log(newStr); // "Frontbencher sits at the Front"
String.prototype.replaceAll()
is trying to bring the full replacement option even when the input pattern is a string.
Private Methods
Private methods can be accessible only inside the class where it is defined. Private method names starts with #
.
class Person { // Private method
#setType() {
console.log("I am Private");
} // Public method
show() {
this.#setType();
}}const personObj = new Person();
personObj.show(); // "I am Private";
personObj.setType(); // TypeError: personObj.setType is not a function
Since setType()
is a private method, personObj.setType
returns undefined
. Trying to use undefined
as a function throws TypeError.
Private Accessors
Accessor functions can be made private by prepending #
to the function name.
class Person {
// Public accessor
get name() { return "Backbencher" }
set name(value) {} // Private accessor
get #age() { return 42 }
set #age(value) {}
}
In the above code get
and set
keywords make name
an accessor property. Even though name
looks like a function, it can be read like a normal property.
const obj = new Person();
console.log(obj.name); // "Backbencher"
console.log(obj.age); // undefined
WeakRef and Finalizers
WeakRef
stands for Weak References. Main use of weak references is to implement caches or mappings to large objects. In such scenarios, we do not want to keep a lot of memory for a long time saving this rarely used cache or mappings. We can allow the memory to be garbage collected soon and later if we need it again, we can generate a fresh cache.
JavaScript is a garbage collected language. If a variable is no longer reachable, JavaScript garbage collector automatically removes it. You can read more on JavaScript garbage collection here in MDN site.
Consider the following code:
const callback = () => {
const aBigObj = {
name: "Backbencher"
};
console.log(aBigObj);
}(async function(){
await new Promise((resolve) => {
setTimeout(() => {
callback();
resolve();
}, 2000);
});
})();
The code might look complicated. But basically, what we do is create a function named callback()
and execute it using setTimeout()
. The async
wrapping is just to use await
functionality. await
is a feature in ES6, that helps to execute asynchronous code in synchronous way.
When executing above code, it prints "Backbencher"
after 2 seconds. Based on how we use the callback()
function, aBigObj
is stored in memory forever, may be.
Let us make aBigObj
a weak reference.
const callback = () => {
const aBigObj = new WeakRef({ name: "Backbencher" }); console.log(aBigObj.deref().name);}(async function(){
await new Promise((resolve) => {
setTimeout(() => {
callback(); // Guaranteed to print "Backbencher"
resolve();
}, 2000);
}); await new Promise((resolve) => {
setTimeout(() => {
callback(); // No Gaurantee that "Backbencher" is printed
resolve();
}, 5000);
});
})();
A WeakRef is created using new WeakRef()
. Later the reference is read using .deref()
method. Inside the async
function, The first setTimeout()
will surely print the value of name
. That is guaranteed in the first turn of event loop after creating the weak reference.
But there is no guarantee that the second setTimeout()
prints "Backbencher"
. It might have been sweeped by the gargage collector. Since the garbage collection works differently in different browsers, we cannot guarantee the output. That is also why, we use WeakRef in situations like managing cache.
Finalizers
FinalizationRegistry
is a companion feature of WeakRef
. It lets programmers register callbacks to be invoked after an object is garbage collected.
const registry = new FinalizationRegistry((value) => {
console.log(value);
});
Here registry
is an instance of FinalizationRegistry
. The callback function passed to FinalizationRegistry
gets triggered when an object is garbage collected.
(function () {
const obj = {};
registry.register(obj, "Backbencher");
})();
Line 3 attaches obj
to registry
. When obj
is garbage collected, the second argument of .register()
method is passed to the callback function. So, according to our code logic, when obj
is garbage collected, "Backbencher"
is passed to the callback function and is printed in the console.
When I executed above code in Google Chrome Canary console, after about 1 min, it printed "Backbencher"
in the console. Another way to force garbage collection in chrome is to click on Collect Garbage icon. We can find it in Performance tab.

Promise.any() and AggregateError
Promise.any()
resolves if any of the supplied promises is resolved. Below we have 3 promises, which resolves at random times.
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));
});
Out of p1
, p2
and p3
, whichever resolves first is taken by Promise.any()
.
(async function() {
const result = await Promise.any([p1, p2, p3]);
console.log(result); // Prints "A", "B" or "C"
})();
What if none of the promises resolve? In that case Promise.any()
throws an AggregateError
exception. We need to catch it and handle it.
const p = new Promise((resolve, reject) => reject());try {
(async function() {
const result = await Promise.any([p]);
console.log(result);
})();
} catch(error) {
console.log(error.errors);
}
For demo purpose, only one promise is passed to Promise.any()
. And that promise is rejected. The above code logs following error in console.

Logical Assignment Operator
Logical assignment operator combines the logical operations(&&
, ||
or ??
) with assignment.
var x = 1;
var y = 2;
x &&= y;console.log(x); // 2
Line 3 operation can be expanded to:
x && (x = y)
Or in other way, it is like:
if(x) {
x = y
}
Since x
is a truthy value, it is assigned with the value of y
, ie 2
.
Just like the way we did with &&
, we can do with ||
and ??
.
x &&= y;
x ||= y;
x ??= y;
Logical assignment operator with ??
??
is Nullish Coalescing operator in JavaScript. It specifically checks if a value is null
or undefined
.
var a;
var b = a ?? 5;
console.log(b); // 5
In line 2, if the value of a
is null
or undefined
, the right hand side of ??
is evaluated and assigned to b
.
Let us now consider ??
along with =
.
var x;
var y = 2;
x ??= y;console.log(x); // 2
Line 2 in the above code is equivalent to:
x = x ?? (x = y)
Here the value of x
is undefined
. So the right hand side expression is evaluated and sets x
to 2
.