一些我不明白的有趣案例。 我代理类并从代理基类扩展子类。
当由于某种原因在
construct
陷阱内构造子类时,错误的原型会被分配给实例 - 基类的原型而不是子类的原型:
class prototype: Child [
"constructor",
"method",
"childMethod"
]
assigned prototype: Base [
"constructor",
"method"
]
Chrome 和 Firefox 中都会发生这种情况。所以它看起来不像是一个错误,而是一切都符合规范。这个问题我不明白为什么。解决方法是手动设置原型(注释行),但谜团仍然存在。
谁能解释一下为什么会发生这种情况:
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();
如果我们手动设置原型,一切都会正常:
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()
将 this
设置为调用它的原始(顶级)构造函数的实例,但这在您的场景中不会发生。如果在 Child
的构造函数中执行以下操作:
constructor() {
super();
console.log(this instanceof Child);
}
您将得到输出
false
。发生这种情况是因为 Base
构造函数的代理由 super()
调用,并且它显式返回 Base
的实例,而没有任何线索表明这实际上是一个 Child
实例。
正如评论中已经解释的那样,super()
的原始
意图的正确执行是将
Reflect.construct
与代理处理程序中的第三个参数一起使用。该处理程序获得第三个参数,告诉您构造实例的预期类型是什么:
construct(_class, args, constructor) {
return Reflect.construct(_class, args, constructor);
}
现在
super()
调用将使用返回的 Child
实例并将 this
设置为它。