显式原型 隐式原型
每个函数都有一个prototype属性
- 如果你自己写一个函数, 那么它默认指向一个Object空对象(即原型对象),你可能说这不是里边有
constructor
和__proto__
吗,这个看了下边的就明白了了。
- 查看一下js提供的array函数的prototype,发现其中已经提供了好多方法,不是空对象了。
console.log(typeof Array.prototype)
console.log(Array.prototype)
原型对象中有一个属性constructor,它指向函数对象。
下图中原型对象的constructor与我声明的fun函数是等价的。因此可以理解为原型对象的constructor与该函数互为引用关系。
给原型对象添加属性(一般都是方法),函数的所有实例对象自动拥有原型中的属性(方法)
上个图里提到的实例中发现__proto__
对象和函数中的prototype
对象内容一样,那二者是什么关系:
- 每个函数function都有一个
prototype
,即显式原型 - 每个实例对象都有一个
__proto__
,可称为隐式原型 - 对象的隐式原型 = 其对应构造函数的显式原型
图解原型(这个说的不全,要完全理解一定要往下看)
当你创建一个函数的时候图解如下:Fun的函数名存储在栈中,堆中会有一个存储Fun的对象。其中有个prototype
属性,此时是个空对象。函数的prototype
显式原型在定义函数时自动添加的, 默认值是一个空Object对象
新建一个Fun的实例fun1,指向的都是同一片地址。 对象的__proto__
隐式原型是创建对象时自动添加的, 默认值为构造函数的prototype属性值。
给Fun函数原型对象添加一个方法,fun1也可以调用。程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
图解原型链
访问一个对象的属性时,先在自身属性中查找,找到返回。如果没有, 再沿着__proto__
这条链向上查找, 找到返回。如果最终没找到, 返回undefined
别名: 隐式原型链
作用: 查找对象的属性(方法)
蓝色线路:fun是Fun的实例,fun调用printhaha
和printaaa
的时候,先在自身找,如果没有,就会循着隐式原型(fun.__proto__
)去找Fun的显示原型(Fun.prototype
)。Fun的显式原型中有printhaha
和printaaa
,调用执行。
橙色线路:fun是Fun的实例,fun调用printhaha
和printaaa
的时候,先在自身找,如果没有,就会循着隐式原型(fun.__proto__
)去找Fun的显示原型(Fun.prototype
)。Fun的显式原型中没有,继续沿着Fun的显式原型的隐式原型(Fun.prototype.__proto__
)去找,最后在Object的显式原型中找到,调用执行。
fun是Fun的实例,fun调用printbbb
的时候,先在自身找,如果没有,就会循着隐式原型(fun.__proto__
)去找Fun的显示原型(Fun.prototype
)。Fun的显式原型中没有,那就会循着Fun的显式原型的隐式原型(Fun.prototype.__proto__
)去找到Object的显式原型(Object.prototype
),如果Object的原型链指向空(Object.prototype.__proto__===null
),没看法往下找了。查找失败,那就不能调用该方法。
总结
先说对象
实例对象的隐式原型指向函数的显式原型。因此所有object的实例的隐式原型都是相同的,都等于Object的显式原型
再说函数
你新建一个函数的时候大部分用的都是下面写的第一种方法,但是也可以用第二种方法声明一个函数。由此可见,你新建的函数也可以看做是Function的一个实例。
所有函数的隐式原型都是指向Function的。因为你定义的函数都算是Function的实例。
- 实例的隐式原型 = 构造函数的显式原型也就是说所有函数的隐式原型都指向Function显式原型,因此所有函数的隐式原型都相等。
- 再加一句:原型对象中有一个属性constructor,它指改函数本身。
图可以看出,只有Function()的显式原型是自己的隐式原型。Function()是他本身的实例。
因此所有函数都是Function()的实例,包括它本身
图中文本框的两句说Object和Function是Function创建的,看起来有点不可思议,但事实确实是这样的。因为实例的隐式原型 = 构造函数的显式原型,下面判断了他们的显式原型和隐式原型也确实是一样的。
但是最神奇的是下边这两句都能表明Function的显式原型的隐式原型都指向Object,也就是说上图我们还可以再加一条线,也就是function Function()
的prototype
的__proto__
指向function Object()
的prototype
。最全的图见补充。
补充:
- 所有函数都是Function()的实例,包括它本身
- Object()的原型对象是原型链的尽头。下边我们可以看出Object的显式原型的隐式原型是null。也就是说原型链到这就到头了。
结合上边总结中的再说函数,我们可以把图再完善一下,这就是终极完善版!!!
- 前边还有写过一句“函数的显式原型指向的对象,默认是一个空Object实例对象”,这个是不严谨的,下图可以看出,Object()的实例对象就不是Object。
- 读取对象的属性值时: 会自动到原型链中查找;设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值。因此,方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上。
下图解释一下嗷:
自定义一个函数,给原型设置上a属性。创建两个Fun的实例。输出他们的a都是aaa,但是通过f2修改了a之后发现只有f2输出会变。
看这个图我们就能理解了。如果查找的时候自身没有,就会顺着原型链去找。但是设置一个值的时候,只会给自身的属性设置,不会修改到原型链。因此方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上。
instanceof
是什么
A instanceof B:如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false。
这句话你就可以根据我上边的终极版图进行对照了。- 例1:这个好理解的,f1的原型链的上一级是Fun,Fun的上一级是Object,Object的上一级是空,所以到Object就到头啦。
- 例2:Function的原型链指向Object的显式原型,Object的原型链指向空值。所以肯定不可能指向Fun了。
- 例3:根据我上边的终极版图进行对照
- 例1:这个好理解的,f1的原型链的上一级是Fun,Fun的上一级是Object,Object的上一级是空,所以到Object就到头啦。
我是安安,我今下午我画图画的超累的!!!