Le 18/09/2013 14:28, Tom Van Cutsem a écrit :
2013/9/17 Jason Orendorff <[email protected] <mailto:[email protected]>>

    If that's all, it seems like we should definitely remove [[Invoke]]
    and the .invoke trap. The MOP was already complicated enough. The
    performance argument is a non-starter, and the other "feature" is
    entirely undesirable.


I'm trying to reconstruct Allen's motivation for including [[Invoke]], which had to do with proxies that forward method calls to target objects that depend on their this-binding to access private state.

Assume that we want to proxy the following target object:

var privateState = new WeakMap();
var target = { m: function() { return privateState.get(this); };
privateState.set(target, 42);
assert( target.m() === 42 );

// I need otherTarget to make a point later
var otherTarget = {};
privateState.set(otherTarget, 43);
assert( target.m.call(otherTarget) === 43 ); // we can still re-bind |this|

Now consider a naive forwarding proxy, written as:

var p1 = new Proxy(target, {
  get: function(target, name, receiver) {
    return target[name];
  }
});

Now: p1.m() === undefined // bad, because |this| inside target.m is bound to proxy instead of target. But: p1.m.call(otherTarget) === 43 // good, clients can still rebind |this|.

To solve the first problem, the proxy can be refined to eagerly bind any method properties:

var p2 = new Proxy(target, {
  get: function(target, name, receiver) {
    var prop = target[name];
    if (typeof prop === "function") return prop.bind(target);
    return prop;
  }
});

Now: p2.m() === 42 // good
But: p2.m.call(otherTarget) !== 43 // bad, cannot re-bind |this| on extracted properties

Adding an invoke() trap gives us a way to solve this:

var p3 = new Proxy(target, {
  get: function(target, name, receiver) {
    return target[name]; // no bind-on-extraction
  },
  invoke: function(target, name, args, receiver) {
    return target[name].apply(target, args);
  }
});

Now: p3.m() === 42 // good
And: p3.m.call(otherTarget) === 43 // good

I guess we could indeed drop the invoke() trap, if we are willing to use the following, more intricate, pattern:

var p4 = new Proxy(target, {
  get: function(target, name, receiver) {
    var prop = target[name];
    if (typeof prop === "function") {
      return function(...args) {
var self = (this === p4 ? target : this); // re-bind |this| only if bound to the proxy, otherwise don't rebind
        return prop.apply(self, args);
      }
    }
    return prop;
  }
});

Now: p4.m() === 42 // good
And: p4.m.call(otherTarget) === 43 // good

AFAICT, performance arguments aside, the question of whether or not to include invoke() boils down to whether you prefer the p3 or p4 pattern.
An alternative to p3 and p4 would be to find a solution for the interaction between private state and proxies that also works with:
    Date.getMonth.call(myProxy)
A way that would make p1 work in essence (which very naturally you listed as the first idea ;-) ).
Such a solution would also make invoke unnecessary.

David
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to