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

eff*_*ffe 6 javascript constructor prototype node.js proxy-pattern

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

function Foo() {}
Foo.prototype.bar = 'baz'
console.log('new Foo().bar: ' + new Foo().bar)
Run Code Online (Sandbox Code Playgroud)
new Foo().bar: baz
Run Code Online (Sandbox Code Playgroud)

现在假设我以某种方式绑定 Foo.绑定函数仍可用于构造函数调用,并this忽略边界:

const Bound = Foo.bind(42)
console.log('new Bound().bar: ' + new Bound().bar)
Run Code Online (Sandbox Code Playgroud)
new Bound().bar: baz
Run Code Online (Sandbox Code Playgroud)

代理应该是一般的和透明的.然而...

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)
Run Code Online (Sandbox Code Playgroud)
new PFoo().bar: baz
new PBound().bar: undefined
Run Code Online (Sandbox Code Playgroud)

我希望第二个代理的行为完全如此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)
Run Code Online (Sandbox Code Playgroud)

eff*_*ffe 3

太长了;博士

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

长答案

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

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

注意:在构造函数调用中,新对象的原型设置为new.target.prototype。这是本规范的第 5 步。

起点:执行 时new F()new.target最初设置为F(跟随链接)。然而,这可能会在施工过程中发生变化......

new Foo()

这里没有什么奇怪的,new.target并且Foo新创建的对象原型是Foo.prototype

new Bound()

这很有趣。一开始,new.targetBound. 然而,绑定函数[[Construct]]内部方法的第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)
Run Code Online (Sandbox Code Playgroud)

个人观点:我理解,在构造绑定函数时,new.target需要进行更改,以便一切按预期工作。同样,我希望new.target在继续之前将代理对象的构造设置为代理函数。这是一个天真的想法,也许还有一些我没有考虑到的极端情况。

  • @rockstar 我找不到任何东西...我想我会在工作组的 GitHub 存储库中创建一个问题,它看起来是一个好地方。 (2认同)