原型链,启动!

67 阅读6分钟

写在前面:本帖用于复习原型链,验证阶段性的学习成果,欢迎大家批评指正!

原型链的设计是为了将公共方法抽离,放在原型对象上,并且使用工厂函数的形式,为每个实例进行初始化的“装饰作用”,私以为可以用组合继承的思想来解释原型链。这样既有原型的内存空间存放公共数据和方法,也有每个实例独有的内存空间,存放特有的数据和方法。

首先,要明确三个概念:原型对象,函数对象,实例对象。

  • 原型对象:每个实例对象都会有一个原型对象,这个原型对象上存放着这个类所共有的属性和方法。

    • 原型对象还有一个重要的属性为construcor,用于指向构造函数,每次创建一个新实例对象时,就会调用这个构造函数,来为这个实例进行初始化,相当于对这个新的毛坯房进行一个装饰,添加一些基本的配置项。

    • 所有对象的始祖是Object,万物之根,所有的对象都是由这个始祖开枝散叶而来。而Object是否有原型对象呢?如有。Object有原型对象,但这个原型对象指向null。(此处描述较为模糊,后面可能更正)。

  • 函数对象(构造函数):函数对象相当于一个“装饰器”,用于初始化实例,为实例添加应有的属性和方法,容易理解,不多赘述。

    • 所有的函数对象的 显式原型属性 (后面会解释,先码住)指向她的原型对象。

    • 特别注意的是,函数对象(构造函数)是一个函数,那么她必定由Funtion创建,所以函数对象(构造函数)的 隐式原型属性(后面会解释,先码住)指向Function的原型对象上。

  • 实例对象:每个由函数对象(构造函数)构建出来的实例身上,会有一个隐式原型属性指向函数对象的显式原型对象。

    • 后面会举个例子来解释这个拗口原理,稍安勿躁。

大家需要记住,原型对象上有constructor属性,指向构造函数;函数对象(构造函数)会有一个显式原型属性指向原型对象;而实例身上会有一个隐式原型属性来指向函数对象(构造函数)的显式原型属性(回答了上文中的码住,大家可以理解为显式与隐式是函数对象和实例特有的属性)。

了解了这些基本原理(开胃小菜),那么开始我们的正题(大餐来了)。

-------------------------------------------------分割线--------------------------------------------------------

只放文字是枯燥的,下面结合图来分别解释一下 object/function/自定义实例对象这三个boss。

万物始祖,Object

image.png 图片是从大图中截取的,请忽略左下方多余的连线,会在文末附上整体图片。

Object原型对象

我们假设,Object对象的存储地址为001。作为万物始祖,Object原型对象的隐式原型对象指向null。之前我们说过,原型对象上必定有constructor属性,指向函数原型(构造函数)。

属性
地址001
__ proto __null
constructor002(Object函数对象)

Object函数对象(构造函数)

紧接着,我们来介绍一下Object对应的函数原型(构造方法),我们假设这个对象的地址为002。之前我们说过,函数原型(构造函数)身上必定有prototype(显式原型属性)指向原型对象,地址001。特别注意的是,函数原型(构造函数)作为函数,肯定是由Function构建而来,所以她也是Function的实例,故存在隐式原型对象指向Function的原型对象。

捋一捋,作为函数原型,具有显式原型属性,指向原型对象。作为函数的实例,具有隐式原型属性,指向Function构造函数的显式原型对象,也就是Function原型对象(别急,等到后面讲Function就明白了)。

属性
地址002
prototype001(Object原型对象)
__ proto __003(Function原型对象)

世界BOSS,Function

所有的方法都是Function的猴子猴孙,所以称她为世界BOSS,看懂Function和Object,原型链理解起来不再话下。

image.png

Function原型对象

我们假设Function原型对象的存储地址为003。

  • 特别注意的是,所有的原型都是Object原型的实例,所以Function原型对象也是Object原型对象的实例,存在一个隐式原型属性指向Object原型对象(实例的隐式原型属性指向构造函数的显式原型对象)。
  • 每个原型对象都会有constructor属性指向构造方法。
属性
地址003
__ proto __001(Object原型对象)
constructor004(Function构造函数)

Function函数对象(构造函数)

  • 作为函数对象(构造函数)必定有prototype属性指向Function原型对象。
  • 特别注意的是,Function函数对象作为构造函数,其也是Function的实例,故存在隐式原型对象指向Function的原型对象。
属性
地址004
prototype003(Function原型对象)
__ proto __003(Function原型对象)

哈喽沃得,MyClass

当我们自定义一个类时,系统开辟一块空间放类的原型对象,我们假设地址为005.

image.png

MyClass原型对象

  • 作为原型对象,是Object原型的实例,所以存在一个隐式原型属性指向Object原型对象(实例的隐式原型属性指向构造函数的显式原型对象)。
  • 每个原型对象都会有constructor属性指向构造方法。
属性
地址005
__ proto __001(Object原型对象)
constructor006(MyClass构造函数)

MyClass函数对象(构造函数)

  • 作为函数对象(构造函数)必定有prototype属性指向MyClass原型对象。
  • 特别注意的是,MyClass函数对象作为构造函数,其也是Function的实例,故存在隐式原型对象指向Function的原型对象。
属性
地址006
prototype005(MyClass原型对象)
__ proto __003(Function原型对象)

MyClass实例对象

“我”终于出现了!作为一个实例,必定存在一个隐式原型属性指向MyClass原型对象(实例的隐式原型属性指向构造函数的显式原型对象)。

属性
地址007
__ proto __005(MyClass原型对象)

大功告成!

image.png

参考文献:

  1. 尚硅谷
  2. juejin.cn/post/725560…