TL:DR
__proto__
is a legacy feature that should generally be avoided MDN
What is __proto__
Simply put, __proto__
is just a propertyKey. And that's really all that you should see it as, because while there is a common behaviour among most builtin objects, It is by no means something that has to (or even should) behave in any specific manner.
Look at it as a regular getter/setter-pair
we can easily make any key behave in the same manner as __proto__
:
const provingAPoint = {
get thisIsANormalKey () { return Reflect.getPrototypeOf(this) },
set thisIsANormalKey (proto) { Reflect.setPrototypeOf(this, proto) }
}
And that's basically all that __proto__
really is.
Always expect an object to differ from the default __proto__
behaviour
If you are recieving an object from some API you should not expect __proto__
to behave like it does in a default object. looking at the above example, it becomes clearer that it's not a reliable thing. We could for example do this:
Reflect.defineProperty(provingAPoint, '__proto__', {
get: () => { throw new Error('you are not getting my prototype like this') },
set: () => { throw new Error('you will not set my prototype like this') }
})
Now if someone tries to access this property, they will get an error. it's now also readonly and can't be changed or deleted.
Use the proper methods instead
The global objects Object
and Reflect
both have the static methods getPrototypeOf
and setPrototypeOf
(not going to go into detail about Reflect
and it's differences). If you're going to manipulate the prototype chain, use these
The single valid use for __proto__
in todays Java/EcmaScript
There is one valid use that's suprisingly not well known. When you create an object literal, if you define a __proto__
property on it, if it's value is an object or null, the resulting object will use that value as it's prototype
// this can save you some of the annoyances of `Object.create(null, {...descriptors})`
const objectWithNullPrototype = {
__proto__: null,
propA: 'valueA',
prop0: 'value0'
}
Object.getPrototypeOf(objectWithNullPrototype) // => `null`
// and again, don't extect any consistent behaviour of `__proto__` because:
objectWithNullPrototype.__proto__ // => `undefined`
About prototype
The prototype
property on a function defines what the is inherited when constructing an object
In short
A constructors prototype
property is equal to the prototype of the object it constructs. eg:
Object.getPrototypeOf(new Array(12)) === Array.prototype // => true
// NOTE: don't do this, use literals or `Object.create`
Object.getPrototypeOf(new Object()) === Object.prototype // => true
Object.getPrototypeOf(new Map()) === Map.prototype // => true
Java/EcmaScript is a prototype-based language
If you have any doubts on what this means, i highly recomend you google and read some articles about it, as misunderstanding (or not knowing) of the implications is a very common source of errors and confusion.