JS中原型和原型链



写在前面

今天学习JS的时候又遇到了原型和原型链的问题,原来记得看过了但是依然一头懵(其实就是忘了),今天又复习了一遍,在这里做个总结,方便以后再次复习,如果有什么错误,还希望大家多多留言指正!


一、概念

1.分类

原型分为显式原型(prototype)和隐式原型(proto)之分,构造函数会有显式原型,默认指向一个空object对象(当然Object的显式原型除外,下面会讲到),实例对象会有隐式原型(这里就不做解释什么是实例对象什么是构造函数了,不知道的小伙伴可以先学习一下再来阅读),隐式原型构造它的构造函数的显式原型。

2.作用

程序员可以通过操纵显式原型来实现属性的继承(这句话非常重要,下面详细解释)

3.图解

在这里插入图片描述


二、原型

首先我们先来看一段代码


function Fn(){
	this.test1=function (){
		console.log('test1')
	}
}

首先这段代码定义了一个简单地函数Fn,内部添加了一个名为test1的变量,并给它赋了一个函数,首先我们应该明白,这是一个构造函数,每一个构造函数当产生时,首先内存中会给它分配一个空间,然后有一个叫Fn的变量指向它,比如下图中,变量Fn中存的是地址0x123指向内存中Fn函数,对于内存中的Fn解析器都会给它添加一个属性叫prototype,这个属性存的是一个地址值,它指向一个空的object对象,然后是用户添加的一个叫test1的方法

在这里插入图片描述

接着我们执行


function Fn(){
	this.test1=function (){
		console.log('test1')
	}
}
Fn.prototype.test2=function(){
    console.log('test2')
}

这里是指给Fn的原型对象添加一个方法为test2,也就是给这个object空对象添加一个方法test2,这个在下图已经体现,这里我们着重看另一个知识点,这个空的object对象是怎么来的,其实是通过object=new Object()产生的,那么这个空object也是一个实例对象,那前面咱们说了实例对象都有隐式原型,那么这个object也要有隐式原型,这里就涉及到一个非常重要的一句话, 实例对象的隐式原型__proto__存的也是一个地址,它指向构造它的构造函数的显式原型所指向的对象。这句话初看非常晕,我们来看这个例子object=new Object(),object是一个实例对象,它是谁构造出来的,是Object()这个函数构造出来,也就是在解析器内部会有这么一个构造函数function Object(){},那么这个Object是一个构造函数,既然是构造函数,就一定有prototype属性,它指向的是Object的原型,所以object空对象的__proto__也指向0x345,这里会有疑问,那么这个Object的原型是不是也是一个空object对象,当然不是,Object的原型就是一切的祖先,他的__proto__是null,它里面已经有解析器给我们封装好的方法比如tostring(),这也就是为什么我们任何对象都能够随时调用toString()f方法的原因,我们通过图进一步来看

在这里插入图片描述

这里明白以后我们接着往下看代码

function Fn(){
	this.test1=function (){
		console.log('test1')
	}
}
Fn.prototype.test2=function(){
    console.log('test2')
}
var fn=new Fn()

这里我们想一想,是不是新建了一个实例对象fn,那么第一步是不是在内存为这个实例对象开辟空间,并创建一个fn的变量指向它,再来想这个fn对象是Fn这个构造函数创造的,那么它会同时拥有Fn的内部函数test1,同时fn是不是会有一个隐式原型属性(proto),里面存的地址值指向构造它的函数Fn的显式原型对象(prototype)所指向的值,来我们把图画出来

在这里插入图片描述

好了我们的图已经画完了,其实总结下来就是两条,1是构造函数自动会有显式原型prototype,而由构造函数new出来的实例对象会有隐式原型proto,2就是理解隐式原型存的地址值是构造出来它的构造函数的显式原型指向的地址,理解了这两句话就OK了,接下来我们到了检验的时候

function Fn(){
	this.test1=function (){
		console.log('test1')
	}
}
Fn.prototype.test2=function(){
    console.log('test2')
}
var fn=new Fn()
fn.test1()
fn.test2()
fn.toString()
fn.test3()

我们一个一个来看,第一个fn.test1()很好理解,他自身就有的,接着就是另外一个知识点,解析器找属性和方法会先在自身的作用域找,如果没有,会沿着隐式原型一层层往上找,直到祖先Object的原型为止,那么我们来看fn.test2(),它首先在自身中找,没有,所以沿着proto往上找,找到了那个空object对象,在里面找到了test2,所以执行,fn.toString()是继续往上找,在祖先中找到了toString()方法,最后fn.test3()我们一路找上去,一直到祖先中也没有找到,所以返回undefind,这就是解析器查找的顺序,有没有清晰很多!


三.高级篇

在理解了上面的以后我们来看一个特殊的,就像刚开始所说,每一个构造函数都有显式原型,比如我们的Fn()和Object(),但是我们进一步思考,这个Fn和Object构造函数是怎么来的呢,不可能是浏览器凭空造出来的呀,其实答案就是,我们在写构造函数比如fnnction Fn(){},其实这个代码就等同于 var Fn=new Function(),以这样的形式创造出来,这么一看,好像Fn又成了一个Function的实例对象了,那Fn是不是也应该有隐式原型,指向Function的显式原型,同时Function作为构造函数也得有一个显式原型指向一个空object对象,这里我们把Fn当做Function的实例对象来画图。

在这里插入图片描述

其实到现在都还好理解,因为和上面聊的其实没有区别,但是套娃开始了,既然Fn是由Fn=new Function()出来的,那Function就也是个构造函数,它是怎么来的呢,这里就是最后一个知识点其实Function是new自己出来的,也就是Function=new Function()出来的,所以注意,Function的隐式原型和显式原型指向的是一个,也就是它的隐式原型等于他自身的显式原型,都是这个object空对象.这里有点绕,但是一定要理解。然后我们再来看看另外一个Object()构造函数,显然,他也是Object=new Function()出来的,所以Object()构造函数的隐式原型指向的Function的显式原型也就是这个空object对象,而这个0x234的空object对象中是不是也要有隐式原型,因为他是一个实例对象,它指向的是构造函数Object()的原型,也就是祖先,所以完全的图应该是这样。

在这里插入图片描述

四、总结

那么原型和原型链有什么用呢,以后我们再想给一个实例化的对象添加方法,我们就可以把这个属性或者方法加到构造函数的显式原型那个空object对象中去,会方便不少,还有其它作用这里就不做过多解释,会在以后的博文中进一步分享。

以上就是原型和原型链,比较绕,如果有哪里写错了,欢迎大家在评论区或者私信我及时指正,不胜感激,犹记得第一次学的时候饶了好久,但是绕出来后就会柳暗花明又一村了。加油!今天的总结就到这里,感谢大家的浏览,希望对您有所帮助!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值