JavaScript Proxies – “Leaky this”

Thursday, November 18th, 2010 by Sebastian Markbåge

You can introduce AOP interception patterns by either changing the original instance or by using additional proxy objects. The later can introduce confusion among instances if not treated correctly. Skip to “Leaky this” if you’re already familiar with proxies in ECMAScript Harmony.

Rewriting vs. Wrapping

Intercepting normal program flows using aspect oriented and other meta-programming patterns can be used for a variety of advanced tricks. Object-relational mapping, logging, security, customized APIs etc.

You can do this by rewriting the methods of the original class/prototype/instance, effectively providing a single instance. You can also do this by providing wrapper objects. That way you have two or more instances.

Rewriting instances or prototypes is easy to do in JS by overriding the functions on existing objects with your own wrapper functions. Although it’s not always possible on the magical host objects. It can also be a problem when these objects have different requirements in various contexts. Such as in a secured sandbox vs. host environment.

Wrappers allow you to leave the original object intact while providing the wrapper to a specific context as if it’s the real object.

Wrappers in ECMAScript 5

In ECMAScript 5 you can already do seemingly perfect wrappers by getting all the properties of an object. Then you define those properties on the wrapper – called a proxy. These are custom getters and setters that delegate to the original object – called the subject.

var subject = new Node();
 
var proxy = Object.create();
 
Object.getOwnPropertyNames(subject).forEach(function(name){
  Object.defineProperty(proxy, name, {
    get: function(name){
      // interception code
      return subject[name];
    }
  }
});

(For simplicity I’ve left out the prototype, setters, enumerations rules etc. Getters are enough to illustrate my point.)

These are fine for frozen objects. The problem is that they don’t represent later changes to the subject. E.g. adding or deleting properties. Additionally, property changes applied to the proxy aren’t propagated back into the subject.

Proxies in ECMAScript Harmony

It looks like the next version of ECMAScript will get support for proxy object that can delegate all operations dynamically – using so called “catch-all” functions.

var subject = new Node();
 
var proxy = Proxy.create({
  get: function(receiver, name){
    // interception code
    return subject[name];
  }
});

When we’re reading a property from the proxy instance, our get-function gets called with the receiver (the proxy instance itself) and the name of the property that should be loaded. We delegate this request to the subject by reading the same property. We can insert any intercepting code as we choose.

This is more efficient and allows for dynamic evaluation of intercepting code. Many ORM patterns – such as Active Record – often use this technique in dynamic environments.

“Leaky this”

However, a problem occurs when these two instances get confused. Imagine this scenario:

var Node = function(){
  var self = this, children = [];
  self.addChild = function(child){
    children.push(child);
    child.parent = self;
    return self;
  };
};

If we wrap an instance of Node in a proxy. Then call:

proxy.addChild(child).addChild(childOther);

We first call addChild through the proxy instance. Our child.parent property is set to the subject instance. The subject instance is returned and we next call addChild directly on the subject instance. Bypassing the proxy.

This is a well-known problem in languages that strongly binds the “this” keyword to a specific instance. Roger Alsing calls this “leaky this” which I find to be an appropriate term since the abstraction is leaking.

However, imagine this JavaScript code:

var Node = function(){
  this.children = [];
};
 
Node.prototype.addChild = function(child){
  this.children.push(child);
  child.parent = this;
  return this;
};

The addChild function now references the “this” keyword. If we repeat the same call in this scenario we get a different result. We call addChild while passing the proxy as the “this” reference. We set the parent property of child to the proxy instance. The proxy instance is returned, and the process is repeated on the second call. This shows the beauty of not having the “this” keyword bound to specific instances.

Although, sometimes you don’t want to use the proxy instance in internal functions. You could use this hybrid to use the subject for whatever use internally, while exposing any proxies externally:

var Node = function(){
  var self = this, children = [];
  self.addChild = function(child){
    children.push(child);
    child.parent = self;
    return this;
  };
};

Conclusion

Brendan Eich points out that unaware consumers of a proxy should be able to treat it just as any other object. This is only true if the provider of both the proxy and the subject have carefully considered the implications of two instances and made sure that no leaking occurs. Code that can be wrapped needs to carefully consider what instance it is currently working with.

Not all code can be wrapped using a proxy indiscriminately.

Tags: , , , ,

27 Responses to “JavaScript Proxies – “Leaky this””

  1. Brendan Eich Says:

    Proxies are not useful only for wrappers, but you are quite right that when wrapping one must take care not to leak the unwrapped object, through any hazard (|this| is just one in JS, alas).

    Firefox uses Proxies internally for its security wrappers, as I mentioned in my blog post, but do implement this as a non-leaky membrane we also use a reference monitor on the ouside of same-origin “compartments”, and a weak map to preserve wrapper/wrappee identity relations. So again your point is well-taken.

    There is a school of thought, Doug Crockford and Mark Miller are exponents of it, that says “don’t use |this|, use lexical scope and nothing else”. This style of JS programming obviously avoids leaky-this.

    /be

  2. Sebastian Markbåge Says:

    I think it’s too general to say “don’t use |this|” since that can cause leaks in certain cases as my examples show. Additionally, it can (obviously) negatively affect performance where you have many instances with many methods – compared to the prototypical model. Each method will need it’s own closure for each instance.

    Thanks for providing your view on this, Brendan. Proxies are indeed awesome for many uses.

  3. Sebastian Markbåge Says:

    To your point about weak maps. I’ve used that pattern myself to implement DOM-like wrappers. To continue my example I can have the proxy cast any wrappee output as the wrapper:

    return ToWrappedNode(subject.addChild(child));

    That way return values never expose unwrapped subjects. However, this still doesn’t solve the problem of subject code calling into external code with itself as an argument. E.g. double dispatch.

    var Node = function(external){
    var self = this;
    self.doWork = function(){
    external.doSomeWork(self);
    };
    };

    Just using pure lexical scope can still be a problem.

  4. Brendan Eich Says:

    Agree that “don’t use |this” is not general enough. See my last comment (comment 7) in my blog, though. Slides 74-78 in my talk show a membraning system that avoids |this| leaks.This handles the double-dispatch case in your last comment. At some cost, of course, but no free lunch.

    /be

  5. Brendan Eich Says:

    Sorry, I’m wrong — deep wrapping via membranes does not solve the external.doSomeWork(self) leak in your last comment — not without putting a membrane around the doWork function or the Node constructor. One area of blindness even for proxies: the upvars captured in a closure. Thanks,

    /be

  6. Sebastian Markbåge Says:

    Right. If the “external” variable was set after it had been wrapped in a membrane it would work. That probably solves most real scenarios.

    The same problem occurs if “external” is a property on the subject (self). So it’s not limited to upvars directly but indirectly through “self”.

  7. Brendan Eich Says:

    The trick with membranes is to wrap a “root” object such as self, or anything purveyed by a constructor by wrapping the ctor, early enough to wrap all the indirectly reachable stuff (transitive closure, wrapped only on demand). Membranes work well that way, but (your point) if the cat is out of the bag, then it’s too late.

    /be

  8. Tom Van Cutsem Says:

    Great article. Another name for the “leaky this” problem coined by Patrick Eugster is the “two body” problem (the fact that proxy and wrapped object have distinct identities). In his paper on “Uniform proxies for Java” he cites it as a problem inherent to the proxy approach. Mark Miller and I acknowledge it in our paper on Javascript proxies, and present membranes as one way to address it. But as both Brendan and you pointed out: membranes only help if you wrap the appropriate initial objects.

  9. Vashti Krumholz Says:

    Your net web page came up in my research and I’m prompted by what you may have composed on this theme. I am presently branching out my enquiry and thus can not contribute additional, however, I’ve bookmarked your site and shall be returning to maintain up with any future updates. Just like it and thanks for allowing my remark.

  10. Bemi Faison Says:

    It seems I needed to make my own Proxy for E4. I was discussing my work on IRC when someone mentioned a native Proxy object… After some Googling I found your page, and I guess this is what they meant.

    However, I don’t see a Proxy object in FireBug currently. I’ll read up more on E5 and how to enable it on a web page – it doesn’t seem to be the default environment.

    Just pointing out that this type of object – irregardless of it’s name – is needed in the non-E5 world. My website link points to my project (of the same name), which might be that answer. Comments and critiques welcome, and I do hope more posts will be made on this subject. I’ve found Proxies to be quite necessary in a world of closured/private libraries.

  11. Anibal Pagon Says:

    I feel that is an interesting position, it made me feel a bit. Thanks for sparking my contemplating cap. From time to time I get so significantly inside a rut that I just feel like a document.

  12. What Happened to Limewire Says:

    It sounds like you are doing problems yourself by trying to find to solve this dilemma rather than looking at why their can be a difficulty in the First place. thanks !!! really very helpful post!

  13. Skinny Jeans For Men Says:

    I am very happy to read this. This is the type of manual that needs to be given and not the accidental misinformation that’s at the other blogs. Appreciate your sharing this best doc.

  14. Subliminal Help Says:

    I’m impressed, I must say. Actually rarely do I encounter a site that’s educative and also enjoyable, and I want to tell you, you may have strike the nail on the head. Your concept is excellent; the difficulty is something that not enough individuals are discussing wisely about. I’m really satisfied that I stumbled throughout this in my seek for something talking about this.

  15. faxless online payday loans Says:

    I am thankful that I discovered this web site, precisely the right information that I was looking for! .

  16. Proxy Software Says:

    Interesting article, I will definitaly mark this blog as one of my favorites.

  17. Ronald Bonato Says:

    I have recently started a web site, the info you offer on this site has helped me greatly. Thanks for all of your time & work. “The inner fire is the most important thing mankind possesses.” by Edith Sodergran.

  18. Antoine Bodenstein Says:

    Hi Michelle Just curious what you mean by A quick skim shows that at least a dozen law firms are “investigating” the deal’ … how do you identify this?

  19. Scottie Sellberg Says:

    It is the best time to make some plans for the future and it is time to be happy. I’ve learn this post and if I could I wish to suggest you few interesting things or suggestions. Maybe you could write next articles referring to this article. I desire to read more issues approximately it!

  20. increase facebook likes for free Says:

    Thanks a lot for sharing this with all people you actually realize what you are speaking approximately! Bookmarked. Please additionally visit my web site =). We will have a link trade arrangement between us

  21. pandora jewelry Says:

    Thanks for all your labor on this web page. My mum really loves carrying out investigation and it’s obvious why. Many of us notice all about the compelling medium you deliver precious tips and tricks via the website and as well invigorate participation from visitors on the area while our own child has always been discovering a whole lot. Take advantage of the rest of the year. You’re conducting a good job.

  22. marek Says:

    you will have an excellent blog right here! would you prefer to make some invite posts on my weblog?

  23. dr renato calabria reviews Says:

    For most recent information you have to pay a visit world wide web and
    on web I found this site as a finest website for most recent updates.

  24. financial information Says:

    Great website you have here but I was curious about if you knew of any forums
    that cover the same topics discussed here? I’d really like to be a part of online community where I can get comments from other knowledgeable individuals that share the same interest. If you have any suggestions, please let me know. Many thanks!

  25. Benjamin Says:

    My spouse and I absolutely love your blog and find
    almost all of your post’s to be what precisely I’m looking for.
    can you offer guest writers to write content in your case?
    I wouldn’t mind producing a post or elaborating on a lot of the subjects you write related to here. Again, awesome website!

  26. webmd health information Says:

    Hello there, You’ve done an incredible job. I’ll certainly digg it and for my part recommend to my friends. I am sure they will be benefited from this website.

  27. financial information Says:

    This paragraph gives clear idea in favor of the new users
    of blogging, that truly how to do running a blog.

Leave a Reply