Proxy .__proto__ is not getPrototypeOf?
not sure I understand but ...
that makes no sense as hasProxyAsProto is not a Proxy
it's inheriting from a Proxy so where is the surprise? it does what I'd expect it to do, pass through the inherited Proxy same as you pass through inherited get/set methods when creating from an object that has get/set behaviors
Moreover, since you inheriting a proxy that reacts on accessors you have
the right behavior when you access __proto__
, the target.__proto__
should be returned instead.
Last, but not least, __proto__
is an Object.prototype
property that has
nothing to do with Object.getPrototypeOf
.... you can redefine
__proto__
behavior either in the Object.prototype
itself, through a
proxy, or within an object, but that won't affect returned value of
Object.getPrototypeOf
var o = {};
Object.defineProperty(o, '__proto__', {
get: function () {
return String.prototype;
}
});
o.__proto__ === String.prototype;
Object.getPrototypeOf(o); // Object.prototype
Best
On May 5, 2014, at 10:40 AM, John Barton wrote:
I'm hoping someone can explain this result which surprises me.
If I create an object with a Proxy-ed prototype, the resulting object does not obey
.__proto__
equalObject.getPrototypeOf()
.For example:
var aPrototype = {foo: 'foo'}; var handler = { get: function(target, name, receiver) { console.log(' (Proxy handler \'get\' called for name = ' + name + ')'); return target[name];
the above line needs to be:
return Reflect.get(target, name, receiver);
Object.prototype.__proto__
is an accessor property and to work correctly it needs to have the originally accessed object passed at the this value. That's why 'get' handlers (and others) have a 'receiver' argument.
}
};
var aProxy = Proxy(aPrototype, handler);
var hasProxyAsProto = Object.create(aProxy);
I tried this on Firefox 29 and Chrome 36. Complete example is here: gist.github.com/johnjbarton/f8a837104f0292fa088c
I can't speak to the correctness of those implementations but you need to have change for it to even have a chance of working.
2014-05-05 19:40 GMT+02:00 John Barton <johnjbarton at google.com>:
I'm hoping someone can explain this result which surprises me.
If I create an object with a Proxy-ed prototype, the resulting object does not obey .proto equal Object.getPrototypeOf().
There's no such equivalence, even for regular objects. __proto__
is just
a property name, so proxy.__proto__
just triggers the "get" trap, as any
normal property access. If the proxy should treat it specially, you need to
special-case the name __proto__
in the "get" trap. However, as Allen
points out, forwarding should work correctly as long as you also forward
the "receiver" binding using the Reflect.get method.
At this point, I expected
hasProxyAsProto.__proto__ === aProxy
but it it not true. Moreover, the operation
hasProxyAsProto.__proto__
calls the Proxy handler get function with
name === '__proto__'
: that makes no sense as hasProxyAsProto is not a Proxy.
If an object inherits from a proxy, then any property access for a non-own property will traverse the prototype chain. If it hits a proxy, that proxy's "get" trap is called.
On Mon, May 5, 2014 at 11:44 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
the above line needs to be:
return Reflect.get(target, name, receiver);
Object.prototype.__proto__
is an accessor property and to work correctly it needs to have the originally accessed object passed at the this value. That's why 'get' handlers (and others) have a 'receiver' argument.
Thanks! With your change the example works. Unfortunately I'm still confused.
Before I reduced the code to a short example I was using
Object.getPrototypeOf(obj)
, like:
gist.github.com/johnjbarton/30c3e72d51d9d64e36d1
rather than targt[name]
my first examples shows. It also fails the same
way.
Let me rephrase my question: why is the Proxy get even called in this case?
On 5/5/14, 4:21 PM, John Barton wrote:
Let me rephrase my question: why is the Proxy get even called in this case?
Because the way __proto__
works is as if the JS implementation had done:
(function() {
protoGetter = Object.getPrototypeOf;
Object.defineProperty(Object.prototype, "__proto__",
{
set: function() { /* magic here */ },
get: function() { return protoGetter(this); }
});
})();
before any page script got to run.
Which means that __proto__
is a simple accessor property on
Object.prototype
. On the one hand, that means that by default it
appears on all objects. On the other hand it means it can be shadowed,
for example by someone doing an explicit defineProperty for that
property name on some object.
But it can also be shadowed by proxies, because those get to intercept
all property access. So when a obj.__proto__
get happens the
implementation walks up the proto chain of obj
looking for a proxy or
an object with an own property named "__proto__"
. If a proxy is found,
its get() is invoked and that's all there is to do as far as the
implementation is concerned. If an own property named "__proto__"
is
found, then the implementation checks whether it's an accessor or value
property, and either returns the value or calls the getter, depending on
which sort it is.
Thanks, Boris, this explanation solves the puzzle for me.
but that's exactly what I've replied to you already?
Once again, __proto__
is spec'd in the Object.prototype
like this:
Object.defineProperty(
Object.prototype,
'__proto__',
{
configurable: true,
get: function () {
return Object.getPrototypeOf(this);
},
set: function (proto) {
Object.setPrototypeOf(this, proto);
}
}
);
the only exception to that specification is as literal syntax so that
{__proto__:[]}
is instanceof Array but {"__proto__":[]}
is not ... that
is the only magic about the property, everything else passes through the
Object.prototype
Best
everything else passes through the
Object.prototype
of course, unless not intercepted or shadowed as shown already in my
previous reply ... Object.defineProperty(anyObject, '__proto__', {value: false})
Since you ask for a response:
On Mon, May 5, 2014 at 11:30 AM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
it's inheriting from a Proxy so where is the surprise? it does what I'd expect it to do, pass through the inherited Proxy same as you pass through inherited get/set methods when creating from an object that has get/set behaviors
I was incorrectly assuming that the proto chain would not be consulted for
__proto__
: it would be found on every object. Boris' post described the
mechanism of __proto__
, then I could see why the get operation traversed
the proto chain and thus why my proxy's getter was called. (IMO, having a
getter high on the proto chain that uses this
(receiver) to return values
only known to the this
object is pretty evil business, even among
notoriously dubious functions like getters.)
May I suggest that trying to guess why someone is surprised is a better strategy than telling them that you are not surprised.
Moreover, since you inheriting a proxy that reacts on accessors you have the right behavior when you access
__proto__
, thetarget.__proto__
should be returned instead.
This makes no sense to me, sorry. Since target
is already obj.__proto__
,
target.__proto__
should not be returned.
Last, but not least,
__proto__
is anObject.prototype
property that has nothing to do withObject.getPrototypeOf
.... you can redefine__proto__
behavior either in theObject.prototype
itself, through a proxy, or within an object, but that won't affect returned value ofObject.getPrototypeOf
Fine but not relevant: the code I am analyzing would, absent my proxy,
always give the same result for Object.getPrototypeOf
and __proto__
. Sure
these could be different, but in reality it doe not happen because too much
code relies on these being the same.
that code is wrong and I don't know why or who would assume that accordingly with specs
I'd rather file a bug in there, pointing at specs ... there are two places
about __proto__
, everything else/other interpretations are evil indeed
I'm hoping someone can explain this result which surprises me.
If I create an object with a Proxy-ed prototype, the resulting object does not obey
.__proto__
equalObject.getPrototypeOf()
.For example:
var aPrototype = {foo: 'foo'}; var handler = { get: function(target, name, receiver) { console.log(' (Proxy handler \'get\' called for name = ' + name + ')'); return target[name]; } }; var aProxy = Proxy(aPrototype, handler); var hasProxyAsProto = Object.create(aProxy);
At this point, I expected
but it it not true. Moreover, the operation
calls the Proxy handler get function with
name === '__proto__'
: that makes no sense as hasProxyAsProto is not a Proxy.I tried this on Firefox 29 and Chrome 36. Complete example is here: gist.github.com/johnjbarton/f8a837104f0292fa088c
Any ideas?