Proxy .__proto__ is not getPrototypeOf?

# John Barton (11 years ago)

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().

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

  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.

I tried this on Firefox 29 and Chrome 36. Complete example is here: gist.github.com/johnjbarton/f8a837104f0292fa088c

Any ideas?

# Andrea Giammarchi (11 years ago)

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

# Allen Wirfs-Brock (11 years ago)

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__ equal Object.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.

# Tom Van Cutsem (11 years ago)

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.

# John Barton (11 years ago)

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?

# Boris Zbarsky (11 years ago)

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.

# John Barton (11 years ago)

Thanks, Boris, this explanation solves the puzzle for me.

# Andrea Giammarchi (11 years ago)

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

# Andrea Giammarchi (11 years ago)

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})

# John Barton (11 years ago)

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__, the target.__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 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

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.

# Andrea Giammarchi (11 years ago)

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