javaScript基础之原型与它的原型链
前言
JavaScript中的原型与原型链作为前端人绕不开的话题,时至今日依然困扰着许许多多的前端小兄弟,例如我之前刚接触这方面的时也感觉云里雾里,后面经过学习之后也是有了自己的理解。
一、什么是原型
原型分为隐形原型和显示原型
所有引用类型(函数,数组,对象)的实例对象都拥有 __proto__属性(隐式原型)
所有函数都拥有prototype属性(显式原型)(仅限函数)
也就是说每个函数都有一个prototype属性,每个对象都有一个__proto__属性。
1.显示原型
在每一个函数里边都有一个prototype的属性,而这个属性指向了prototype对象,这个prototype就是显示原型,我们来看一下实例:
2.隐式原型
在JavaScript中,每个对象其实也有一个[[prototype]]属性,但是主流浏览器都用__proto__代表[[prototype]]属性,这个属性指向一个对象,而__proto__我们就叫隐式原型
小结:如何区分显示原型和隐式原型
简单来说我们将函数的原型称为显示原型,将对象的原型称为隐式原型,此时我们就能得出一个结论:实例对象的隐式原型 === 构造函数的显式原型。当然,函数也是对象,函数也具有隐式原型,函数的隐式原型指向构造该函数对象的构造函数的显式原型,比如构造函数
function Foo()的隐式原型指向Function.prototype.
二、什么是原型链
原型链顾名思义就是原型和原型连在一起形成链,实例对象在查找属性时,如果查找不到,就会沿着
__proto__去与对象关联的原型上查找,如果还查找不到,就去找原型的原型,直至查到最顶层,在javaScript中有一句话叫做万物皆对象,所以Object.prototype就是顶端了,它的隐式原型就指向了null,如果找到这里也就结束了返回空对象,这也就是原型链的概念。
下面引入一张原型和原型链领域的宝图,一图看懂就能理解令人疑惑的原型和原型链。 如果开头上这张图那大家就和看天书差不多了,但是上面已经介绍了隐式原型和显示原型的基本概念,以及简单讲解了原型链的概念,相信大家还是能粗略的看懂一部分。
我们从上到下分析一下这张图。
- 首先
function Foo()的显示原型是Foo.prototype,function Foo()的隐式原型是就应该是构造函数Function.prototype,这是因为所有function函数是通过Function函数构造出来的 - 这里我们可以发现构造函数
Foo()的原型prototype和 构造函数Foo()所创建出来的对象实例f1,f2的原型__proto__是一样的都指向Foo.prototype,(实例对象的隐式原型 === 构造函数的显式原型。) . 而Foo.prototype也是一个对象所以它的隐式原型__ proto __就是Object.prototype. - 接下来我们发现有个地方好像和其他地方不太一样,为什么
function Function()和Function.prototype会形成环装结构,首先因为Function也是一个函数,函数是一种对象,也有隐式原型属性。既然是函数,那么它一定是被Function创建。所以function Function()的显式原型一定是Function.prototype,我相信这个大家都没什么问题,怎么理解function Function()的隐式原型还是Function.prototype呢? - 既然是函数那么就是被Function函数构造出来的,那么我们可以把
function Function()看成一个实例对象,实例对象的隐式原型就是构造函数Function的显式原型;而Function.prototype也是一个对象所以它的隐式原型就是Object.prototype - 最后由于对象也是被构造函数创建出来的,所以
function Object()的隐式原型就是构造函数的显式原型Function.prototype,当然函数对象的显式原型就是Object.prototype,俗话说万物皆对象,所以Object.prototype就是顶端了,它的隐式原型就指向了null。
三、constructor属性
细心的同学已经发现了,上面关系图里面有一个叫
constructor的属性,它到底是是什么意思呢。其实根据那个关系图可以大概知道constructor指向的是原型对象的构造器的引用,其实就是指向函数本身,但是我们不能完全把constructor理解为“由...构造”,这里的指向我们如果修改了,是会影响到所有通过此构造器生成的实例的,prototype的作用是继承和共享属性用的,他的作用范围会影响所有实例,下面看一段代码:
看起来是不是很神奇,
Object(...)并没有构造a1对吧?看起来应该是Foo()"构造"了它。如果是这样的话a1.constructor应该是Foo,但是它并不是Foo! 这到底是是怎么回事?其实a1并没有.constructor属性,所以它会沿着原型链找到Foo.prototype,但是Foo.prototype的.constructor属性只是Foo函数在声明时得默认属性。如果创建了一个新对象替换了函数默认的.prototype的对象引用,那么新对象并不会自动获得.constructor属性,所以这个对象现在也没有.constructor属性(默认的Foo.prototype有这个属性,且Foo.prototype.constructor默认指向Foo),所以会继续沿着原型链找,找到了顶端的Object.prototype.这个对象有.constructor属性,指向了内置的Object(...)属性,所以就出现了上面那样的情况。
结语
这是我刚开始写的第一篇文章,如果有什么不正确或者不小心误导大家的地方希望大家可以对我进行批评纠错,通过这篇文章的分享我相信你对原型以及原型链都有了一定的了解,原型和原型链也算是前端人头上的大山之一,面试也经常会有,希望在今后的学习中与君共勉。