11

While reading on Javascript's prototypes I encountered this behaviour I can't explain. I am testing this in chrome's console (which is V8).

var fruit = {taste:'good'};
var banana = Object.create(fruit);
console.log(banana.taste); //"good"
console.log(banana.__proto__); //Object {taste: "good"}
console.log(Object.getPrototypeOf(banana)); //Object {taste: "good"}

So far everything is as expected. However if I do this:

var drink = Object.create(null);
Object.defineProperty(drink, 'taste', {value:"nice"});
var coke = Object.create(drink);
console.log(coke.taste); //"nice"
console.log(coke.__proto__); //undefined
console.log(Object.getPrototypeOf(coke)); //Object {taste: "nice"}

then coke.__proto__ === undefined. Why is it undefined in the second case?

Kaarel Purde
  • 1,255
  • 4
  • 18
  • 38
  • 1
    So this isn't actually a bug. __proto__ is undefined because __proto__ comes from Object.prototype. In the second case Object.prototype is not in the prototype chain, because chain ends with null – Kaarel Purde Feb 17 '16 at 13:42
  • possible duplicate of [Is `__proto__` safe to use as a property name for objects created using `Object.create(null)`?](http://stackoverflow.com/q/22512329/1048572) or [Why the prototype can be retrieved but the `__proto__` is undefined in JavaScript?](http://stackoverflow.com/q/31159621/1048572) – Bergi Feb 17 '16 at 14:17
  • @Bergi I think this is the cleanest of the question of the bunch, and I'd nominate it for a master duplicate. However, I answered the question (but I also answered one of your proposed duplicates as well) so I might be biased. I'd also be happy to take it to JavaScript chat or meta, to get a few more voices, if you think that's better. – apsillers Feb 17 '16 at 14:24
  • @apsillers: Actually I don't mind whether they're closed as dupes or not (rather not, they're not exactly the same), I only wanted to interlink them. I just used the standard slang in a comment, instead of voting to close (which would have hammered them). – Bergi Feb 17 '16 at 14:27

1 Answers1

13

I once opened an issue for this behavior, but it was closed as standards-compliant behavior. According to the issue's close reason:

This is working as specified. ES6 __proto__ is a getter defined on Object.prototype. For an object that doesn't have that in its prototype chain it is not accessible (just like, say, hasOwnProperty isn't). You need to use Object.getPrototypeOf instead.

This is indeed true: ES6 section B.2.2.1 defines Object.prototype.__proto__; therefore, the __proto__ property is inherited from Object.prototype. However, your drink object was created with Object.create(null), so it doesn't have Object.prototype in its prototype chain.

An object always has internal knowledge to its prototype, stored in its [[Prototype]] internal slot. The __proto__ property is a way to access that internally-known prototype through code. An object's lack of a __proto__ property does not affect its [[Prototype]] slot, which still exists.

To be perfectly clear: coke does have a prototype (stored in [[Prototype]]), and that prototype is the object drink. You can see this with Object.getPrototypeOf(coke). However, that is the entire prototype chain, because the prototype of drink is null. Therefore, coke can't inherit __proto__ from Object.prototype.__proto__ because it doesn't have Object.prototype in its prototype chain.

Perspicacious
  • 594
  • 3
  • 17
apsillers
  • 112,806
  • 17
  • 235
  • 239
  • I thought that `__proto__` is the actual reference to the prototype object. If `__proto__ === undefined` then how `Object.getPrototypeOf()` knows that `drink` is `coke`'s prototype? I am guessing V8 internally doesn't use `__proto__` then – Kaarel Purde Feb 17 '16 at 13:45
  • @potato300 I just asked the same question. The `coke` has a `prototype` indeed, the `drink` object. But the `drink` object doesn't have a prototype (it is `null`), so the link to `__proto__` cannot be reached by the chain. – Dmitri Pavlutin Feb 17 '16 at 13:48
  • 1
    @potato300 Correct; it uses [[Prototype]], which is a internal slot that allows the object to always know its own prototype. The [[Prototype]] value is not directly accessible through code. – apsillers Feb 17 '16 at 13:53