JavaScript函数覆盖原型

4
我在SO上找到了一些有趣的例子,其中包括指向这篇文章的链接。 文章中提到:

Function.__proto__ 指向 Function.prototype。这导致:

 Function.constructor === Function

也就是说:函数本身就是它自己的构造函数!
Object instanceof Object == true.

这是因为:
Object.__proto__.__proto__.constructor == Object 

请注意,与 Object instanceof Object 不同,Foo instanceof Foo == false。这是因为:Foo 不存在于自己的原型链构造函数中。
从 Mozilla 开发者网络得知,原型和构造函数都可以很容易地被覆盖。而且,由于 instanceof 只检查原型链中的 constructor.prototype,我不明白为什么我的代码仍然返回 false
function Foo() { } ;
Foo.prototype = Foo
Foo.constructor = Foo
Foo instanceof Foo // still false

关于原型,还有一些小问题。我理解的是,原型本身是一个独立的辅助对象?而这个对象就像是指向另一个对象(通常是Object)的指针。

2个回答

4
obj instanceof 不是在 obj.prototype 中查找 constructor,而是在对象内部的 __proto__ 属性中查找。
JavaScript 中的每个对象都有一个内部的 __proto__ 属性,它指向对象的原型。当使用 new 运算符构造对象时,该对象的内部 __proto__ 属性会被设置为构造函数的 prototype 属性。
因此,当你说 Foo instanceof Foo 时,JavaScript 虚拟机会在 Foo.__proto__ 中查找 "Foo"。由于 Foo 是一个函数,Foo.__proto__Function.prototype(其中 Function 是函数的构造函数)。
由于你无法实际更改对象的内部 __proto__ 属性,所以 Foo 永远不可能是 Foo 的实例。
关于您的小问题:在 JavaScript 中,一切都是对象,包括原型和函数。事实上,ECMAScript 5 添加了一个函数 Object.createMDN),它以一个对象作为其第一个参数,并创建一个新对象,该对象使用第一个对象作为其内部原型对象。

嗯...有人能告诉我如何将粗体的__proto__变成__proto__吗? - Bart
抱歉回答有重叠,你的回答很好。我不知道如何避免粗体标记,除了像你之前在回答中所做的那样将其包装在代码段中。 - Hannes Johansson
谢谢您的回答,确实是这样。我尝试更改__proto__,但JS虚拟机显示了对象循环的错误。 - Johnny_D
1
@Johnny_D __proto__ 链永远不可能是循环的。如果您在对象中查找属性(例如 obj.value),但该属性不存在,则会在对象的 __proto__ 中查找该属性。如果该属性也不存在,则会查找 __proto__.__proto__,直到找到该属性或 __proto__ 为 null。对于循环原型链,这将导致无限递归。 - Bart
同意,但在 Function 对象的情况下,它会以某种方式处理。 - Johnny_D

1
重要的是要记住,对象实际上的内部原型引用是原型链的一部分,与对象的原型属性不同。对象的内部原型设置为其构造函数的原型属性。 当我学习这个问题时,我发现最易懂的文章实际上是this one。 换句话说,只有在对象是应该用作构造函数的函数时,才改变对象的原型属性才有意义。除此之外,它只是一个属性,没有什么神奇之处。更改原型不会更改__proto__,也不会影响对象的原型链。 因此,浏览代码:
function Foo() {}

此时Foo的构造函数实际上是Function,而Function.prototype是Foo的实际内部原型,或者说是__proto__

Foo.prototype = Foo;

这只改变了Foo的原型属性,但没有改变其内部原型。
Foo.constructor = Foo

这实际上只在 Foo 上设置构造函数属性,它不会对 Foo.prototype.constructor 做任何事情,也不会对 Foo 的内部原型的构造函数进行任何操作,而这是 instanceof 检查的内容。

尝试这段代码,希望它会更加清晰:

function Foo() { } ;

(Foo.prototype != Function.prototype && Foo.__proto__ == Function.prototype);

Foo.prototype = Foo;

(Foo.prototype == Foo && Foo.__proto__ != Foo && Foo.__proto__ == Function.prototype);

Foo.constructor = Foo;

(Foo.constructor == Foo && Foo.prototype.constructor == Foo && Foo.__proto__.constructor != Foo);

感谢这篇文章和评论,现在对我来说看起来更整洁了。 - Johnny_D
似乎在你的最后一个例子中应该是 Foo.prototype.constructor == Foo - Johnny_D
是的,谢谢。一个函数的prototype.constructor总是指向函数本身,除非显式地进行了更改。 - Hannes Johansson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接