Proxy forwarding handlers and accessor properties

David Flanagan dflanagan at mozilla.com
Thu Jun 16 16:54:15 PDT 2011


With ordinary objects, methods and property getter functions are both 
invoked on the object itself.  If this object is proxied with 
Proxy.Handler, however, the property getter function will be invoked on 
the original object and the method will be invoked on the proxy object.  
The code below demonstrates.

I find this surprising, though I'm not sure whether it constitutes a bug 
in Proxy.Handler.  I do think it is at least a reason why the receiver 
argument to get() is necessary and should not be removed as proposed by 
http://wiki.ecmascript.org/doku.php?id=strawman:proxy_drop_receiver

The code that demonstrates this is below.  It runs in the current 
Firefox Aurora. I have actually been affected by this issue when the 
proxy object is stored in a WeakMap but the object it is forwarding to 
is not in the WeakMap.

     David Flanagan


if (this.console) print = console.log.bind(console);

// An object with an accessor property and a method.
var o = {
     get property() { return map.get(this); },
     method: function() { return map.get(this); }
};

var map = new WeakMap();
map.set(o, "o");

// Both the getter function and method are invoked on o
print(o.property);         // prints "o"
print(o.method());         // prints "o"

// This works for inherited properties and methods, too
var p = Object.create(o);
map.set(p, "p");
print(p.property);        // prints "p"
print(p.method());        // ditto


// When we create a proxy with a forwarding handler, though, the this value
// is different in the two cases.
var handler = {
     target: o,
     get: function(receiver, name) {
         return this.target[name];  // Same as Proxy.Handler.prototype.get
     }
}

var q = Proxy.create(handler);
map.set(q, "q");
print(q.property);   // Prints "o"
print(q.method());   // Prints "q"

// In order to invoke the getter function on the same object as the method
// we have to go to more trouble and use code that is probably much slower.
// And, we need that first receiver argument.
var handler2 = {
     target: o,
     get: function(receiver, name) {
         var d = Object.getOwnPropertyDescriptor(this.target, name);
         if (d.value) return d.value;
         else return d.get.call(receiver);
     }
};

var r = Proxy.create(handler2);
map.set(r, "r");
print(r.property);   // Prints "r"
print(r.method());   // Prints "r"




More information about the es-discuss mailing list