2013/9/17 Jason Orendorff <[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.
Cheers,
Tom
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss