注意:首先,__proto__
是非标准的,使用Object.getPrototypeOf
,虽然是简短的,而且因为我很懒,所以我prototype
在这个答案中确实使用了很多次
好吧,如果我没看错的话,您会被最初的假设所抛弃:
Foo.prototype -> Foo{}
(它是一个构造函数)的原型Foo
,根据定义是Foo
. 但是,因为构造函数是一个函数,并且构造函数返回一个特定实例的对象,所以原型将是一种增强型Object
。
我不太喜欢将原型惯性与经典 OOP 进行比较,但可以这样想:
Foo
原型是一种可变的类定义(您可以随时添加方法和属性),构造函数创建一个扩充此原型的对象,并在实例级别添加另一层属性/方法。
因此Foo.prototype instanceof Foo
是真的。但是,Foo
作为一个对象:
Object.getPrototypeOf(Foo.prototype) === Object.prototype
是真的。正如
Object.getPrototypeOf(Array.prototype) === Object.prototype.
把它翻译成 JS (prototype)-speak,你会得到类似的东西:
对于 JS 创建的每个函数对象,都会为该对象分配一个prototype
属性。该prototype
属性是 的一个实例Object
,但它具有 1 个特殊属性。
如果尝试访问对象上的属性或方法,而 JS 找不到在实例级别定义的该属性,则 JS 将尝试解析原型属性上的名称:
instance.nonExistantProperty;
//js scans instance variable, doesn't find property:
instance.prototype.nonExistantProperty
//js uses prototype, if not found:
instance.prototype.prototype.nonExistantProperty
//all the way up to Object.prototype, which does not have a prototype, so here an exception is raised (TypeError is thrown)
这是此查找的简短示意图,这是我从我的一篇较早的帖子中复制的,可能值得一看,还可以查看底部的链接答案,它们会详细介绍此问题
[ F.divide ]<=========================================================\ \
F[divide] ===> JS checks instance for property divide | |
/\ || | |
|| || --> property found @instance, return value-------------------------------| |
|| || | |
|| ===========> Function.prototype.divide could not be found, check prototype | |
|| || | |
|| ||--> property found @Function.prototype, return-----------------------| |
|| || | |
|| ==========> Object.prototype.divide: not found check prototype? | |
|| || | |
|| ||--> property found @Object.prototype, return---------------------|_|
|| || |=|
|| =======>prototype is null, return "undefined.divide"~~~~~~~~~~~~~~~|X|
|| \ /
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~< TypeError can't read property 'x' of undefined
基本上就是这样。
现在,也许您对为什么Foo.prototype.constructor
引用该Foo
函数也有些困惑。
同样,这很容易,因为每个实例都应该包含您可能需要的所有信息,以确定您正在处理的对象类型:
function Foo{}
var inst = new Foo();
console.log(inst.constructor);//references the constructor function
请记住,返回的所有实例都Foo
返回一个对象,该对象引用所有prototype
属性,以及(可选)实例级别的一些属性。
那么,你为什么还要费心创建这样的实例呢?同样,这非常简单:更改实例,不会更改原型:
console.log(Foo.prototype.constructor === inst.constructor);//true
inst.constructor = function(){};//override constructor property
console.log(Foo.prototype.constructor === inst.constructor);//false, prototype did not change