Search code examples
javascriptprototypeprototypal-inheritance

What don't objects own __proto__ which is explicity set?


I understand that __proto__ is defined on Object.prototype so normal objects shouldn't own it.

But why don't object literals own __proto__ even if it is explicitly set?

var obj = {
  __proto__: 'hello',
  normal_prop: 'world'
};

obj.hasOwnProperty('__proto__');   // false
obj.hasOwnProperty('normal_prop'); // true

Also strangely,

obj.__proto___ // {}

I thought this was due to __proto__ being assigned a non-object, but:

var obj = {
  __proto__: {value: 42},
};

obj.__proto__; // { value: 42 }
obj.hasOwnProperty('__proto__');   // false

obj.__proto__ = {value: 'hello world'}
obj.__proto__;     // { value: 'hello world' }
obj.hasOwnProperty('__proto__');   // false

I see people referring to __proto__ as a "pseudo property", and I guess this might be the reason, but I can't find details. I read the section on __proto__ in the ES6 spec but it didn't help much.

This behavior is present in the current version of Firefox and Chrome.

Where should I be looking at?


Solution

  • Setting obj.__proto__ is the equivalent of using Object.setPrototypeOf(obj, ...). The setter function is defined that way, according to the standard. So when you assign __proto__, you're in fact doing the same thing as when you call Object.setPrototypeOf. You're not really giving a value to a __proto__ property, you're assigning a prototype to your object.

    You can see it easily like this:

    obj = {
     __proto__: {value: 42}
    };
    
    Object.setPrototypeOf(obj, {value: 43});
    
    console.log(obj.__proto__); // { value: 43 }

    If you want to go deeper, you can see details in v8 (Chrome javascript engine) source code, here: https://chromium.googlesource.com/v8/v8/+/refs/heads/4.2.76/src/v8natives.js

    You'll see that the setter for __proto__ is basically the same thing as Object.setPrototypeOf.

    You can even mimic the behavior:

    obj = {
      __proto__: {
        value: 42
      }
    };
    
    Object.defineProperty(obj, 'fakeProto', {
      set: function(value) {
        Object.setPrototypeOf(this, value)
      }
    })
    
    obj.fakeProto = {
      value: 43
    };
    
    console.log(obj.__proto__); // { value: 43 }

    Obviously the last example is not exactly what happens with __proto__, it's just to show that some properties can have setter functions that don't simply assign a value. And the standard says that the setter for __proto__ needs to do the same thing as setPrototypeOf.