[[Invoke]] and implicit method calls

David Bruant bruant.d at gmail.com
Wed Sep 18 10:01:06 PDT 2013


Le 18/09/2013 14:28, Tom Van Cutsem a écrit :
> 2013/9/17 Jason Orendorff <jason.orendorff at gmail.com 
> <mailto:jason.orendorff at gmail.com>>
>
>     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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130918/709ae0b1/attachment-0001.html>


More information about the es-discuss mailing list