对象不拥有什么 __proto__ 是显式设置的?

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

我知道 __proto__ 是在 Object.prototype 上定义的,所以普通对象不应该拥有它。

但为什么对象字面值不拥有 __proto__,即使它已明确设置?

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

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

也奇怪,

obj.__proto___ // {}

我认为这是因为 __proto__ 被分配了一个非对象,但是:

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

我看到有人将 __proto__ 称为 "pseudo property",我想这可能是原因,但我找不到详细信息。我阅读了 ES6 规范中关于 __proto__ 的部分,但没有太大帮助。

此行为存在于当前版本的 Firefox 和 Chrome。

我应该看哪里?

设置obj.__proto__等同于使用Object.setPrototypeOf(obj, ...)。 根据标准,setter 函数就是这样定义的。因此,当您分配 __proto__ 时,您实际上在做与调用 Object.setPrototypeOf 时相同的事情。您并没有真正为 __proto__ 属性 赋值,您是在为对象分配原型。

你可以很容易地看到它是这样的:

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

Object.setPrototypeOf(obj, {value: 43});

console.log(obj.__proto__); // { value: 43 }

如果你想更深入,你可以在v8(Chrome javascript 引擎)源代码中查看详细信息,这里: https://chromium.googlesource.com/v8/v8/+/refs/heads/4.2.76/src/v8natives.js

您会看到 __proto__ 的 setter 与 Object.setPrototypeOf 基本相同。

您甚至可以模仿以下行为:

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 }

显然,最后一个示例与 __proto__ 的情况并不完全相同,它只是为了表明某些属性可以具有 setter 函数,而不仅仅是赋值。标准说 __proto__ 的 setter 需要做与 setPrototypeOf.

相同的事情