难以手动走原型链

Difficulty Manually Walking The Prototype Chain

我想尝试手动遍历几个对象的原型链,看看我在这个过程中找到了什么。但是,我卡在了我尝试的第一个上。这是代码:

function MyObject() { }
var x = new MyObject();
console.log('--------------------------------------------');
console.log('x.constructor.name: ' + x.constructor.name);
console.log('x.constructor.prototype.constructor.name: ' + x.constructor.prototype.constructor.name);
console.log(x.constructor.prototype === Function.prototype ? 'Good guess.' : 'No, you are wrong.');
console.log(x.constructor === MyObject ? 'Good guess.' : 'No, you are wrong.');
console.log('--------------------------------------------');

以上代码在 Google Chrome 的开发人员工具控制台中产生以下输出:

--------------------------------------------
x.constructor.name: MyObject
x.constructor.prototype.constructor.name: MyObject
No, you are wrong.
Good guess.
--------------------------------------------

x 的构造函数是 MyObject 函数是有道理的,因为 x 是使用 MyObject 上的 new 关键字实例化的(这遵循构造函数的定义)。因此,我理解了第一行输出(注意:我开始从零开始计算输出行数)。然而,第二行让我感到困惑。我想知道 MyObject 的原型是什么。显然,它不是函数类型的对象,如输出的第 3 行所示,这告诉我我错了。第四行输出只是加强了第一行的输出。

更笼统地说,我假设遍历对象原型链的正确方法是从有问题的对象到它的构造函数,然后从构造函数到构造函数的原型,假设最后一个引用不为空。我假设应该重复这个两步过程(包括转到构造函数,然后转到构造函数的原型),直到达到从构造函数到原型的空引用,从而表示原型链的结尾。不过,这似乎不起作用,因为将此算法应用于上述场景只会导致循环引用。

综上所述,我有两个问题:

  1. 为什么是x.constructor === x.constructor.prototype.constructor(或者换句话说,为什么是循环引用),x.constructor是一个什么样的对象。无论如何,原型(或者换句话说,它是如何实例化的/它从哪里来的)?
  2. 如何修正上述算法才能正确遍历对象 x 的原型链?

编辑

我在 Whosebug here 上遇到了类似的问题,但它没有明确询问遍历原型链的正确方法。它确实指出了循环引用,但是...

是的,一开始可能有点难以掌握。我不能比为您提供一些链接做得更好。这些总是在我遇到麻烦时帮助我。

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

http://mckoss.com/jscript/object.htm

http://zeekat.nl/articles/constructors-considered-mildly-confusing.html

Q1:对于 "why",请参阅上面的参考资料。 x.constructor.prototypex.__proto__,它是 x 的内部 "real" 原型,至少在没有 prototypeconstructor 属性被覆盖的情况下。它是在您定义函数 MyObject 时创建的。

Q2:很遗憾,您不能这样做。您可以在支持的地方使用 __proto__ 属性,但请参阅

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

在 Javascript 术语中,对象 a 的 "prototype" 是指 a 从中继承属性的对象。基于标准的访问方式是 Object.getPrototypeOf:

var protoOfA = Object.getPrototypeOf(a);

还有旧方法,非标准但受某些浏览器支持:

var protoOfA = a.__proto__;

但是如果你有一个函数 F,F.prototype 不会引用 F 从中继承任何东西的对象。相反,它指的是 F 创建的 实例继承的对象:

function F() {};
a = new F();
console.log(Object.getPrototypeOf(a) === F.prototype); // true

当你定义一个函数时,会创建一个对象作为该函数创建的实例的原型,这个新对象存储在函数的prototype 属性.

--

函数在很多方面表现得像对象(例如,它们可以具有属性)但它们与其他对象并不完全相同:

console.log(typeof a); // "object"
console.log(typeof F); // "function"

他们的"prototypes"定义不明确(例如Chrome中的运行)(这显然是一个Chrome-specific behavior

console.log(Object.getPrototypeOf(F)); // "function Empty() {}"
console.log(Empty);                    // ReferenceError: Empty is not defined

--

constructor 属性 很奇怪。 The interpreter doesn't care about it. MDN says, confusingly:

Returns a reference to the Object function that created the instance's prototype.

此外,您可以更改对象上 constructor 的值,但这对对象是什么或它的行为方式没有影响 - 它只是描述性的。

--

所以,回答你的问题:

Why is x.constructor === x.constructor.prototype.constructor

没有充分的理由。这是浏览器收敛的任意行为。

what kind of an object is x.constructor.prototype, anyway

在这个例子中,t是x的原型,与Object.getPrototypeOf(x)相同。但总的来说,你不能依赖 x.constructor 或任何派生自它的东西,因为它是任意的。

How can the above algorithm be corrected in order to correctly walk the prototype chain for object x?

for (var p = x ; p != null ; p = Object.getPrototypeOf(p)) {
  // do something with p
}