Reputation: 2589
Given class
class Test {
test() {
console.log('test called');
}
}
And some object toExtend = {}
How can I extend this object so it will have test
method?
Object.assign
( as well as _.extend
, _.assign
, $.extend
) do not copy methods. What is preferable way to do that?
Note that toExtend
is passed from outside
UPD:
toExtend
is instance of another class and has it's own prototype's methods
Upvotes: 2
Views: 179
Reputation: 48277
For object literals, which start with no protoype of their own (Object.getPrototypeOf(toExtend) === Object.protoype)
), you can simply use Object.setPrototypeOf
to extend the object:
class Test {
test() {
console.log('test called');
}
}
const toExtend = {};
// Set the prototype, so you "inherit" methods:
Object.setPrototypeOf(toExtend, Test.prototype);
toExtend.test();
In older runtimes, you would have to manually assign the prototype:
function Test() {
// noop ctor
}
Test.prototype.test = function() {
console.log('test called');
};
var toExtend = {};
// Set the prototype, so you "inherit" methods:
toExtend.__proto__ = Test.prototype;
toExtend.test();
For instances of an existing class, things are significantly more complex. They do have a prototype of their own, potentially with properties that must be copied, so you need to walk through those:
class Foo {
test() {
console.log('test');
}
}
class Bar {
toast() {
console.log('toast');
}
}
function dynamicExtend(target, base) {
const baseProto = Object.getPrototypeOf(target);
if (baseProto == Object.prototype) {
// simple case: no existing prototype
Object.setPrototypeOf(target, base.prototype);
} else {
// complex case: existing prototype
const proxyClass = class extends base {};
const proxyProto = proxyClass.prototype;
// assign the target properties
Object.getOwnPropertyNames(baseProto).forEach(n => {
const desc = Object.getOwnPropertyDescriptor(baseProto, n);
Object.defineProperty(proxyProto, n, desc);
});
Object.setPrototypeOf(target, proxyProto);
}
}
const targets = [{},
new Bar()
];
targets.forEach(t => {
dynamicExtend(t, Foo);
t.test();
if (t.toast) {
t.toast();
}
});
Note that, thanks to the proxy class, this does break instanceof
style inheritance checks.
__proto__
As @PatrickRoberts noted in the comments, __proto__
is deprecated, so you should prefer setPrototypeOf
whenever possible.
Upvotes: 9