Reputation: 6790
Although not finalized, I am experimenting with ES6 Proxies. My goal is to have a constructor function (one that utilizes classical inheritance like the one shown below) for creating proxy objects with inheritance chains.
function inherit(child, parent){ //a classical inheritance pattern
var F = function(){};
F.prototype = parent.prototype;
child.prototype = new F();
child.parent = parent.prototype;
child.prototype.constructor = child;
return child;
}
Does the following look sensible? I create an item and return the proxy from the constructor. Any method that would return a reference to the instance (for chaining purposes) must instead return a reference to the proxy, otherwise we lose the proxy while chaining.
function Item(attrs){
this.attrs = attrs;
var proto = this.constructor.prototype;
return this.proxy = Proxy.create(new MyHandler(this), proto);
}
Item.prototype.setStatus = function(status){
//do work
return this.proxy; //we do this everywhere instead of a simple 'this'?
}
function DVD(attrs){
attrs.type = 'DVD';
return Item.call(this, attrs);
}
inherit(DVD, Item);
var negotiator = new DVD({title: 'The Negotiator'}); //returns proxy
The goal is to construct proxied objects (via the 'new' keyword) that may be products of a classical inheritance chain.
Next issue. Consider what happens if I extend my Item prototype with Backbone.Events. Those imported methods are going to return this
instead of this.proxy
. To get around this, I must wrap all the imported methods returning this
in order to return the proxy
instead. This seems painful and error prone. Making use of the Proxy abstraction involves a lot of wiring, which doesn't seem quite right to me.
While I love the Proxy concept, I am concerned with its practicality. Maybe I am missing some better practice for meeting my goals?
EDIT:
This example was oversimplified and thus failed to describe why I needed it.
Ultimately, my goal for using proxies is to allow for multiple inheritance. I want to be able to mixin multiple behaviors (modules) into a given proxy object. I also want it to be simple to unmix behaviors at whim.
By using proxies, I can have the Proxy object manage access to methods and as needed pass information along to the mixins. Basically, this allows for improved aspect-oriented programming. With an aspect, I would would typically wrap and replace a function. Sometimes a given method is wrapped and rewrapped by several aspects. The trouble with AOP is once you've modified an object in this way it's not easy to remove just a single aspect because that aspect may have been buried by rewrapping.
Proxies don't suffer this issue. Your mixins can instead sit in an array. The proxy handles dispatching to methods found in these mixins (even multiple methods, thus multiple inheritance). Then unmixing behavior is as easy as removing a mixin from that array.
The trouble with proxies is that while it is a common practice for a method to return a reference to the object itself (for chaining purposes), you don't really want any of the original methods to return a reference to that object (thus bypassing the proxy), you want the proxy returned instead.
Upvotes: 4
Views: 3093
Reputation: 943
But if you want just to create proxy function like $.proxy does it, but don't want to use jQuery you can do like this:
var context = this;
something.addSomeListener( function(){ context.myListener.apply( context, arguments ); } );
Upvotes: 0
Reputation: 19987
Your goal of creating mixins using proxies is not a bad idea (see this blog post). The problem you indicate however, which I am taking is your main question, is actually not a problem at all. This code for example:
var o, p;
function Obj() { }
Obj.prototype.whoAmI = function() {
console.log("I am the "+ (this == o ? "object" : "proxy"));
return this;
};
o = new Obj();
var p = new Proxy(o, {});
o.whoAmI().whoAmI();
p.whoAmI().whoAmI();
will output:
I am the object
I am the object
I am the proxy
I am the proxy
As you can see that the this
keyword that is used within the target objects is actually the proxy. Thus if the target object returns this
it actually returns the proxy. Exactly what you wanted.
If you wanted to be absolutely sure that the proxy object is returned you could wrap your proxied methods like this:
var o, p;
function Obj() { }
Obj.prototype.whoAmI = function() {
console.log("I am the "+ (this == o ? "object" : "proxy"));
return o; // bit of a code smell here
};
o = new Obj();
var p = new Proxy(o, {
get: function(target, name) {
return function() {
var rtnVal = target[name].call(this);
return rtnVal == target ? this : rtnVal;
}
}
});
p.whoAmI().whoAmI();
which will output:
I am the proxy
I am the proxy
This ensures that the object returned is never the unwrapped object.
All this is of course not very conducive to the performance of your method calls. Instead I would advise a more traditional approach to javascript mixins. There are not many really good use-cases for turning mixins on and off on the fly as far as I know.
Upvotes: 1