Some interesting case which I don't understand. I proxy classes and extend a child class from a proxied base class.
When a child is constructed inside the construct trap for some reason a wrong prototype is assigned to an instance - the base class' prototype instead of a child class' prototype:
class prototype: Child [
"constructor",
"method",
"childMethod"
]
assigned prototype: Base [
"constructor",
"method"
]
This happens both in Chrome and Firefox. So it's not looking like a bug but rather everything to the spec. The problem I cannot understand why. The fix is to set the prototype manually (the commented line), but the mystery remains.
Could anyone explain why this happens:
const proxy = what => new Proxy(what, {
construct(_class, args, constructor) {
const obj = new _class(...args);
console.log('class prototype:', _class.name, Object.getOwnPropertyNames(_class.prototype));
console.log('assigned prototype:', obj.__proto__.constructor.name, Object.getOwnPropertyNames(obj.__proto__));
// for some reason we need this if a class is proxied
//Object.setPrototypeOf(obj, _class.prototype);
return obj;
}
});
const Base = proxy(class Base {
isBase = true;
method(){
console.log('Base method');
}
});
const Child = proxy(class Child extends Base { // extends from a proxy
isChild = true;
method() {
console.log('Child method');
super.method();
}
childMethod(){}
});
const base = new Base;
const child = new Child;
console.log('--------- EXECUTING METHODS ---------');
base.method();
child.method();
If we set the prototype manually everything works fine:
const proxy = what => new Proxy(what, {
construct(_class, args, constructor) {
const obj = new _class(...args);
// for some reason we need this if a class is proxied
Object.setPrototypeOf(obj, _class.prototype);
return obj;
}
});
const Base = proxy(class Base {
isBase = true;
method(){
console.log('Base method');
}
});
const Child = proxy(class Child extends Base { // extends from a proxy
isChild = true;
method() {
console.log('Child method');
super.method();
}
childMethod(){}
});
const base = new Base;
const child = new Child;
console.log('--------- EXECUTING METHODS ---------');
base.method();
child.method();
super()is expected to setthisto an instance of the original (top-level) constructor it is called from, but this does not happen in your scenario. If in the constructor ofChildyou do this:You'll get as output
false. This happens because the proxy of theBaseconstructor is invoked bysuper()and it explicitly returns an instance ofBasewithout any clue that this was actually intended to be aChildinstance.As already explained in comments, the correct execution of the original intent of
super()is to useReflect.constructwith the third argument in your proxy handler. That handler gets a third argument that tells you what the intended type was of constructed instance:Now that
super()call will use that returnedChildinstance and setthisto it.