你不知道的 JavaScript 系列上, 54 - 检查 [[Prototype]]

instanceof

function Foo() {
  // ...
}
Foo.prototype.blah = ...;
var a = new Foo();

我们如何找到 a 的 “祖先” (委托关联)呢?第一种方法:

a instanceof Foo; // true

instanceof 操作符的左操作符是一个普通的对象,右操作数是一个函数。 instanceof 回答的问题是:在 a 的整条 [[Prototype]] 链中是否有指向 Foo.prototype 的对象?可惜,这个方法只能处理对象和函数之间的关系。如果想判断两个对象之间是否通过 [[Prototype]] 链关联,只用 instanceof 无法实现。

isPrototypeOf

Foo.prototype.isPrototypeOf(a); // true

isPrototypeOf 回答的问题是:在 a 的整条 [[Prototype]] 链中是否出现过 Foo.prototype

这里我们只需要两个对象就可以判断它们之间的关系。举例来说:

// 非常简单: b 是否出现在 c 的 [[Prototype]]链中?

b.isPrototypeOf(c)

这个方法并不需要使用函数,它直接使用 b 和 c 之间的对象引用来判断它们的关系。

getPrototypeOf

我们也可以直接获取一个对象的 [[Prototype]] 链。在 ES5 中,标准的方法是:

Object.getPrototypeOf(a);

Object.getPrototypeOf(a) === Foo.prototype; // true

__proto__

绝大多数浏览器也支持一种非标准的方法来访问内部 [[Prototype]] 属性:

a.__proto__ === Foo.prototype; // true

这个奇怪的 __proto__ 属性 “神奇地” 引用了内部的 [[Prototype]] 对象。这个和 .constructor 一样,实际上并不存在于正在使用的对象中,它和其他的常用函数(.toString(), .isPrototypeof(...)等等)一样,存在于内置的 Object.prototype中。它们是不可枚举的

__proto__ 看起来很像一个属性,但是实际上它更像一个 getter/setter。实际上大致是这样的

Object.definePrototype(Object.prototype, "__proto__", {
  get: function() {
    return Object.getPrototypeOf(this);
  },
  set: function() {
    // ES6中的 setPrototypeOf(...)
    Object.setPrototypeOf(this, o);
    return o;
  }
})

因此,访问 a.__proto__ 实际上是调用了 getter 函数,虽然 getter 函数存在于 Object.prototype 对象中,但是它的 this 指向对象 a,所以和 Object.getPrototypeOf(a) 结果相同。