英文:
A proxied JS child class assigns a wrong prototype to an instance
问题
我是你的中文翻译助手,以下是翻译好的内容:
有一个我不理解的有趣案例。
我代理类并从代理的基类扩展一个子类。
当一个子类在construct
陷阱中被构造时,由于某种原因,一个错误的原型被分配给实例 - 基类的原型而不是子类的原型:
class prototype: Child [
"constructor",
"method",
"childMethod"
]
assigned prototype: Base [
"constructor",
"method"
]
这在Chrome和Firefox中都会发生。所以这看起来不像是一个bug,而是符合规范的一切。我无法理解的问题是为什么会发生这种情况。修复方法是手动设置原型(注释掉的那一行),但是这个谜团仍然存在。
有人能解释一下为什么会发生这种情况吗:
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();
英文:
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:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
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();
<!-- end snippet -->
If we set the prototype manually everything works fine:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
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();
<!-- end snippet -->
答案1
得分: 2
super()
预期将this
设置为调用它的原始(顶级)构造函数的实例,但在您的情况下并没有发生这种情况。如果在Child
的构造函数中执行以下操作:
constructor() {
super();
console.log(this instanceof Child);
}
你将得到输出false
。这是因为super()
调用了Base
构造函数的代理,并且它明确返回一个Base
实例,没有任何线索表明这实际上是一个Child
实例。
如前所述,在代理处理程序中正确执行super()
的原始意图是使用Reflect.construct
和第三个参数。该处理程序获得第三个参数,该参数告诉您构造的实例的预期类型:
construct(_class, args, constructor) {
return Reflect.construct(_class, args, constructor);
}
现在,super()
调用将使用返回的Child
实例并将this
设置为它。
英文:
super()
is expected to set this
to an instance of the original (top-level) constructor it is called from, but this does not happen in your scenario. If in the constructor of Child
you do this:
constructor() {
super();
console.log(this instanceof Child);
}
You'll get as output false
. This happens because the proxy of the Base
constructor is invoked by super()
and it explicitly returns an instance of Base
without any clue that this was actually intended to be a Child
instance.
As already explained in comments, the correct execution of the original intent of super()
is to use Reflect.construct
with the third argument in your proxy handler. That handler gets a third argument that tells you what the intended type was of constructed instance:
construct(_class, args, constructor) {
return Reflect.construct(_class, args, constructor);
}
Now that super()
call will use that returned Child
instance and set this
to it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论