Monkey Patching
What you want to do is called monkey patching?—?you mutate a built-in prototype. There are many wrong ways to do it, but I’ll demonstrate a way that is currently the most correct way.
Methods
In your case, the function body should return this.b
. You can get the object itself with the this
keyword.
If you really want to monkey-patch, check for the function’s existence beforehand to ensure forward-compatibility and make the property writable, configurable, and non-enumerable (the last of which is the default when using defineProperty
). Extracting a method makes getB
a non-constructible function. All this ensures that getB
behaves very much like existing built-in methods (or methods provided by the host environment).
if(!Object.hasOwn(Object.prototype, "getB")){
Object.defineProperty(Object.prototype, "getB", {
writable: true,
configurable: true,
value: {
getB(){
return this.b;
}
}.getB
});
}
Object.hasOwn
is quite a new method, but in older environments it can simply be replaced by Object.prototype.hasOwnProperty.call
or a variant thereof. If you can’t support methods, use this instead:
if(!Object.prototype.hasOwnProperty("getB")){
Object.defineProperty(Object.prototype, "getB", {
writable: true,
configurable: true,
value: function getB(){
return this.b;
}
});
}
Keep in mind that arrow functions cannot be used for this purpose since they don’t have their own this
binding.
Getters / Setters
An alternative is to use a getter. Consider this:
const arr = [
"a",
"b",
"c",
];
console.log(arr.indexOfB); // 1
How would an indexOfB
getter on the Array
prototype look like? We can’t use the above approach and replace value
by get
, or else we’ll get:
TypeError
: property descriptors must not specify a value or be writable when a getter or setter has been specified
The property writable
needs to be removed entirely from the descriptor. Now value
can be replaced by get
:
if(!Array.prototype.hasOwnProperty("indexOfB")){
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB
});
}
A setter can also be specified by adding a set
property to the descriptor:
if(!Array.prototype.hasOwnProperty("indexOfB")){
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB,
set: {
indexOfB(newValue){
// `newValue` is the assigned value.
// Use `this` for the current Array instance.
// No `return` necessary.
}
}.indexOfB
});
}