构造函数调用绑定函数的代理

Constructor call to proxy of a bound function

假设我有一个函数 Foo,我希望从它构造的对象具有 bar 属性:

function Foo() {}
Foo.prototype.bar = 'baz'
console.log('new Foo().bar: ' + new Foo().bar)

new Foo().bar: baz

现在假设我 bind Foo 以某种方式。绑定函数仍然可以在构造函数调用中使用,绑定的this被忽略:

const Bound = Foo.bind(42)
console.log('new Bound().bar: ' + new Bound().bar)

new Bound().bar: baz

Proxies 应该是通用和透明的。然而...

const PFoo = new Proxy(Foo, { })
console.log('new PFoo().bar: ' + new PFoo().bar)

const PBound = new Proxy(Bound, { })
console.log('new PBound().bar: ' + new PBound().bar)

new PFoo().bar: baz
new PBound().bar: undefined

我希望第二个代理的行为与 Bound 完全相同,因为我使用的是空处理程序。换句话说,我希望最后的输出是 baz.

为什么不是这样?

(完整片段如下)

function Foo() {}
Foo.prototype.bar = 'baz'
console.log('new Foo().bar: ' + new Foo().bar)

const Bound = Foo.bind(42)
console.log('new Bound().bar: ' + new Bound().bar)

const PFoo = new Proxy(Foo, { })
console.log('new PFoo().bar: ' + new PFoo().bar)

const PBound = new Proxy(Bound, { })
console.log('new PBound().bar: ' + new PBound().bar)

tl;博士

当使用new F时,new.target设置为F 除非 F是绑定函数,在那种情况下new.target 成为原始函数。代理不会发生这种情况。

长答案

好的,我想我明白了。 是一个很好的起点。主要成分:

  • new.target
  • [[原型]] 内部插槽
  • 构造函数prototype属性

注意:在构造函数调用中,新对象的原型设置为new.target.prototype。这是 this specification.

的第 5 步

起点:在执行 new F()new.target is initially set to F 时(点击链接)。但是,这可能会在构建过程中发生变化...

new Foo()

没什么奇怪的,new.targetFoo,新创建的对象原型是Foo.prototype

new Bound()

这很有趣。一开始,new.target就是Bound。但是,bound functions [[Construct]] internal method 的第 5 步执行以下操作:如果将 new.target 设置为绑定函数,则将其更改为目标函数,即 Foo。因此,再次使用Foo.prototype

new PFoo()

new.target 始终是 PFoo,但它是 Foo 的代理,因此当请求 PFoo.prototype 时,再次给出 Foo.prototype

new PBound()

new.target 设置为 PBound。这次调用绑定函数的[[Construct]]内部方法时,new.target等于绑定函数,所以没有改变,我们最终使用 PBound.prototype,转发到 Bound.prototype。确实...

function Foo() { }
Foo.prototype.iAm = 'Foo'
const Bound = Foo.bind(42)
Bound.prototype = {iAm: 'Bound'}
const Proxied = new Proxy(Bound, { })
console.log(new Proxied().iAm)

个人意见:我的理解是,在构造绑定函数时,new.target 被更改,以便一切都按预期工作。同样,我希望代理对象的构造在继续之前将 new.target 设置为代理函数。这是一个幼稚的想法,也许还有我没有考虑的极端情况。