JavaScript 详细图解原型

显式原型 隐式原型

每个函数都有一个prototype属性

  • 如果你自己写一个函数, 那么它默认指向一个Object空对象(即原型对象),你可能说这不是里边有constructor__proto__吗,这个看了下边的就明白了了。
    在这里插入图片描述
  • 查看一下js提供的array函数的prototype,发现其中已经提供了好多方法,不是空对象了。
			console.log(typeof Array.prototype)
			console.log(Array.prototype)

在这里插入图片描述
原型对象中有一个属性constructor,它指向函数对象。
下图中原型对象的constructor与我声明的fun函数是等价的。因此可以理解为原型对象的constructor与该函数互为引用关系。
在这里插入图片描述
在这里插入图片描述
给原型对象添加属性(一般都是方法),函数的所有实例对象自动拥有原型中的属性(方法)
在这里插入图片描述
上个图里提到的实例中发现__proto__对象和函数中的prototype对象内容一样,那二者是什么关系:

  1. 每个函数function都有一个prototype,即显式原型
  2. 每个实例对象都有一个__proto__,可称为隐式原型
  3. 对象的隐式原型 = 其对应构造函数的显式原型
    在这里插入图片描述

图解原型(这个说的不全,要完全理解一定要往下看)

当你创建一个函数的时候图解如下:Fun的函数名存储在栈中,堆中会有一个存储Fun的对象。其中有个prototype属性,此时是个空对象。函数的prototype显式原型在定义函数时自动添加的, 默认值是一个空Object对象
在这里插入图片描述
新建一个Fun的实例fun1,指向的都是同一片地址。 对象的__proto__隐式原型是创建对象时自动添加的, 默认值为构造函数的prototype属性值。
在这里插入图片描述
给Fun函数原型对象添加一个方法,fun1也可以调用。程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
在这里插入图片描述

图解原型链

访问一个对象的属性时,先在自身属性中查找,找到返回。如果没有, 再沿着__proto__这条链向上查找, 找到返回。如果最终没找到, 返回undefined
别名: 隐式原型链
作用: 查找对象的属性(方法)
在这里插入图片描述
在这里插入图片描述
蓝色线路:fun是Fun的实例,fun调用printhahaprintaaa的时候,先在自身找,如果没有,就会循着隐式原型(fun.__proto__)去找Fun的显示原型(Fun.prototype)。Fun的显式原型中有printhahaprintaaa,调用执行。
橙色线路:fun是Fun的实例,fun调用printhahaprintaaa的时候,先在自身找,如果没有,就会循着隐式原型(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。最全的图见补充。
在这里插入图片描述

补充:

  1. 所有函数都是Function()的实例,包括它本身
    在这里插入图片描述
  2. Object()的原型对象是原型链的尽头。下边我们可以看出Object的显式原型的隐式原型是null。也就是说原型链到这就到头了。
    在这里插入图片描述
    结合上边总结中的再说函数,我们可以把图再完善一下,这就是终极完善版!!!
    在这里插入图片描述
  3. 前边还有写过一句“函数的显式原型指向的对象,默认是一个空Object实例对象”,这个是不严谨的,下图可以看出,Object()的实例对象就不是Object。
    在这里插入图片描述
  4. 读取对象的属性值时: 会自动到原型链中查找;设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值。因此,方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
    下图解释一下嗷:
    自定义一个函数,给原型设置上a属性。创建两个Fun的实例。输出他们的a都是aaa,但是通过f2修改了a之后发现只有f2输出会变。
    在这里插入图片描述
    看这个图我们就能理解了。如果查找的时候自身没有,就会顺着原型链去找。但是设置一个值的时候,只会给自身的属性设置,不会修改到原型链。因此方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上。
    在这里插入图片描述
  5. instanceof是什么
    A instanceof B:如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false。
    这句话你就可以根据我上边的终极版图进行对照了。
    • 例1:这个好理解的,f1的原型链的上一级是Fun,Fun的上一级是Object,Object的上一级是空,所以到Object就到头啦。
      在这里插入图片描述
    • 例2:Function的原型链指向Object的显式原型,Object的原型链指向空值。所以肯定不可能指向Fun了。
      在这里插入图片描述
    • 例3:根据我上边的终极版图进行对照
      在这里插入图片描述

我是安安,我今下午我画图画的超累的!!!

发布了122 篇原创文章 · 获赞 423 · 访问量 52万+

猜你喜欢

转载自blog.csdn.net/qq_36667170/article/details/105099064