js原型链看这篇就够了

1,070 阅读6分钟

谈到js原型大家会想到什么,是不是会发出这样的感叹,我明明好好的去看了啊,为什么我还是不懂,我真是太难了,知识装不进脑子啊,本人其实一开始也是这个样子,看了许多关于解释原型,原型链的帖子,什么原型的栗子,什么造物者上帝啊巴拉巴拉,但是还是云里雾里,好像懂了,好像不懂,经过本人这段时间对原型的仔细研究,终于能通过理解画出原型链的死亡之图了,放图

死亡之图???,是的,因为当时自以为原型链嘛我懂了,简单(牛皮吹到天上去了),再看这张图,我emmmm什么都没看懂,瞬间打脸,所以本人形象的称他为原型链之死亡之图🙃🙃🙃

准备好了吗,我要开始画图了(ps:我可是没有死记硬背哦,本人也想死记硬背,但是这知识进不了脑子啊,所以一般知识点都靠理解,我这脑子都能懂,你们也可以的😀)

第一部分 构造函数 && 原型对象

function Animal(name,age) {
    this.name = name
    this.age = age
}

let pig = new Animal('peiqi',3)
console.log(pig)

我们看上面的例子,我们创建了一个大写的Animal()函数,并且里面定义了一些属性,然后new了一个Animal,并且把它赋值给了pig,像这个大写的函数Animal就是一个构造函数,它可以通过new创建它的实例对象,其中pig就是Animal的实例对象,当然你还可以new其他的实例对象比如说bird,dog等等,每个构造函数都有一个prototype属性,我们称之为显式原型对象,每个实例对象都有一个proto属性,我们称之为隐式原型对象,他们都是指向同一个原型对象(敲黑板!!!)

微信图片_20200316151047.png

我们可以看到,红框中的内容是一摸一样的,

上面的代码就相当于这个样子,现在听我解释:

  1. 函数其实是个对象,但是这个对象是可以加()运行的,而普通的对象,比如说{a:1},是不可以加括号来执行的,我们把可以执行的对象称之为函数对象
  2. 对象,函数,这种我们都知道,他是个引用类型,引用类型在内存中是从栈中获取该对象的内存地址,然后再从堆内存中取得所需要的数据
    image.png
  3. 上面的图,Animal是个函数,他在栈上存了一个叫Animal的变量对应的是堆上的一个函数对象,他在堆上内存地址为0x111(就是栈上存了一个堆上的地址),Animal是一个构造函数,所以他有一个prototype的属性(就是显式原型对象),这个属性是一个普通的对象,因此他也需要在堆上在开辟一个新的内存存prototype对象,存的地址为0x222,而他在Animal函数对象上,只存了prototype对象的地址
  4. pig是通过Animal构造函数new出来的一个实例对象,他在栈上也存了一个叫做pig的变量,对应的是堆上的一个函数对象,他在堆上的内存地址为0x333,实例对象身上都有一个__proto__属性(隐式原型对象),他也是一个对象,并且构造函数的显式原型对象 === 实例对象的隐式原型对象 所以__proto__ 指向0x222

第二部分 原型链到哪结束

image.png
从上图来看,我们可以清晰的看到,_ proto _ 只到Object就没有了,通过下图,我们来捋一遍Object与其他构造函数之间的关系
image.png

  1. 任何的对象都是new Object()出来的
  2. Object()也是一个构造函数,所以他在栈上存了一个Object的变量,对应的是堆上的函数对象,这个对象在堆上的内存地址是0x444,并且里面还装了一个prototype属性(构造函数身上必有一个prototype属性),他在堆内存中也开了一块地址,去存他的prototype,内存地址为0x555
  3. 原型对象(不管是显式原型对象还是隐式原型对象)都是普通对象,他不能加()运行,他相当于是通过new Object()产生的,所以所有的普通对象都有一个__proto__属性,并且普通对象._ proto _=== Object.prototype
  4. Animal的显式原型对象也是普通对象,所以他的显式原型对象中有一个__proto__属性,它指向Object的显式原型对象

你可能会问那Object的显式原型对象不是也是对象吗,他身上不是应该也有个__proto__属性,指向Object的显式原型对象,但是这个样子就会出现死循环,没有出口出去,所以Object.prototype._ proto _= null

第三部分 function Animal() 与 function Function()

我们知道不管是什么函数,都是相当于 new Function(),所以普通的构造函数Animal() 和 Function()之间存在着联系,我们看下图

image.png

构造函数Animal相当于 new Function()出来的,所以此时Animal()是Function()的实例对象,之前说过只要是实例对象,必定存在__proto__属性,所以Animal._ proto _ 指向Function()的显式原型对象,即 Animal._ proto _ === Function.prototype

第四部分 function Function() && Object.prototype 关系大乱炖(这是原型链中最难理解的一部分)

image.png

  1. function Object()也是个函数,所以我们可以认为Object()是通过 new Function() 出来的,所以Object()此时是实例对象,实例对象上面一定有__proto__属性,所以Object._ proto _ === Function.prototype
  2. function Function()同样也是函数,我们也可以认为他是通过 new Function() 出来的,所以Function()此时是实例对象,实例对象上面一定有__proto__属性,所以Function._ proto _ === Function.prototype
  3. 我们说过任何东西都是new Object()所出来的,所以Function() 也是new Object() 出来的,此时Function()是实例对象,实例对象上面一定有__proto__属性,但是Function的__proto__属性的连线已经连到了Function的显式原型对象,所以它通过上图的红线进行连接,Function._ proto . proto _=== Object.prototype

结束 && 总结

通过这么几张图,其实我们已经把那张死亡之图画完了(把我画的那几张图都连起来就ok了),不信你们可以比较一下我们自己画的图和那张图,只是表现形式有所不同,我们只要理解以下几点:

  1. 函数也是对象,跟普通函数相比,它可以加()运行
  2. 构造函数身上一定能有prototype属性
  3. 实例对象身上一定有__proto__属性
  4. function Object() 和 function Functiton() 也是函数可以通过 new Function() 创建
  5. function Function() 是函数对象,他也是对象,所以可以通过 new Object() 创建

如果能理解以上的几点,画出死亡之图真的很轻松哦✌✌✌

结束语

好啦,我把我如何理解的画原型链的图告诉你们了,可以自己动手画一下,如果有什么问题或者错误可以私信我哦🤞🤞🤞