原型链是什么?关于原型链中constructor、prototype及__proto__之间关系的认识

原型链关系图

首先,我们暂且把object类型和function类型分开来,因为 function是一个特殊的对象类型,我们这里这是便于区分,把function类型单独拿出来。顺便一提,typeof也是单独把function设为一种类型,我们可以用typeof来验证我们的猜想。

从上图中可以看到,所有的橘色箭头(constructor)都指向function Function(){},所有的黑色箭头(__proto__)开始有两条路可以走,但是最后都汇聚在了一起,指向null。而天蓝色箭头(prototype)不具有连续性,一般都是一个function指向一个object。然后我们可以得出以下结论:

  1. 所有对象的构造函数(constructor)最终指向function Function( ){ }这个函数,包括他自己。ps:为了方便,我们称它为母Function。

  2. 所有对象的__proto__最终指向null,即一切原型链的末端就是null。

  3. 所谓的一切源于对象,这里的”对象”指的是Object{}(红色虚线框内,也就是Object的prototype)。ps:为了方便,我们在这里叫它母Object。

  4. 除了Object的原型(prototype)直接指向母Object,其他所有类型的(构造器的)原型(prototype)都先指向一个由母Object派生出来的一个子Object,再指向母Object,Function的原型为function{}。ps:构造器,即构造函数。

  5. 哲学部分:这幅图包含两个”先有鸡还是先有蛋”的问题:

    • function(){}是由母Function构造的,但它同时又是母Function的原型。。

    • function(){}的隐式原型(__proto__)是母Object,母Object是由构造函数 function Object(){}构造的,但函数function Object(){}的隐式原型又是function (){}。。。。
      当然除了”先有鸡还是先有蛋”,原型链中还有一个最最有哲理的问题:

    • 构造函数function Function(){}的构造函数就是他自己,就是说,他自己把自己生下来了。。。。。 = =

  6. 至于为什么function(){}有俩个框框,其实是因为它是原型中最特殊的。它是原型中唯一一个的function类型的,其他的原型都是object类型。可能是考虑到它也是由构造函数Function生成的吧,所以typeof返回的值也是function。

我们再来说一下可能大家都很疑惑的问题,就是原型(prototype)和隐式原型(__proto__)的区别。

我说一下我的见解。相对于”原型”,我觉得”__proto__”更适合叫做父对象,因为在原型链中,负责连接各个对象的,就是”__proto__”。也就是说,我改写对象的”prototype”,并不会影响对象在原型链中的位置,想脱离或者改变原型链,只能是改写”__proto__”。

在原型链中,对象和它的”__proto__”指向的对象的关系,更像是别的语言中的子类和父类。子类会继承父类的方法和属性,而且会和父类保持一样的类型。

一般来说,”__proto__”都是指向某一个对象的”prototype”属性,所以对象会继承的也就是其父对象”prototype”中的属性和方法,但是并不会继承其父对象自身的属性方法。说的可能有点绕,我们举个栗子:

//以Object为原型创建一个对象,obj。
var obj=new Object
/*这句话可以翻译为:
var obj={}
obj.__proto__=Object.prototype
*/

这个时候,obj的”__proto__”指向的是Object.prototype,所以obj的父对象是Object.prototype,obj会继承Object.prototype中的属性方法,但是并不会继承Object中的属性和方法。这时候我们用obj.toString()是可以的,但是用obj.length就会报错。这是因为toString()是写在Object.prototype里面的,而length是写在Object里面的。
在学习运算符的时候,相比很多初学者也会有和我一样的疑问,为什么instanceof向上查找会在prototype里查找,而不在”父对象”中查找。理解我上面所说,大家相比也会明白了吧。就拿上面的例子来说,其实Object只是obj的构造器,构造函数而已,obj真正的父对象是Object.prototype。
那讲到这里有的小伙伴就会有疑问了,那我要是想继承Object里面的属性怎么办?其实也很简单,设置obj的”__proto__”指向Object就可以了。不过有一点,”__proto__”属性毕竟是底杠开头,是官方不想暴露在外面的属性,能不用的时候最好不要用。其实就算不用”__proto__”也是可以达到想要的效果的。Object里面有一个可以创建对象的方法create,用它我们可以轻松达到我们的要求。举个栗子:

//以Object为原型创建一个对象,obj。
var obj= Object.create(Object)
/*这句话可以翻译为:
var obj={}
obj.__proto__=Object
再说一下create的用法:
Object.create(prototype,[{code}]);//返回一个对象
可以看到,这里有两个参数,第一个参数prototype相当于创建对象的__proto__,值得话随便一个对象就可以了,第二个参数可以不填,里面详细写创建对象的一些属性和他们的属性标签。注意,create创建的是一个对象,和new一样,无论以什么为父对象,返回值都是object。
*/

再来说说prototype,通过上图大家会发现,prototype属性只是一个函数和一个对象之间的一个桥梁,在这里为什么要说是桥梁呢,为什么不说是函数的一个属性呢?我曾写过一个例子:

function fa(){}
fa.prototype.fname=’fa’
var ch=new fa()
ch.fname        //’fa’
fa.prototype={fname:’newfa’} //改写prototype
ch.fname        //依旧是’fa’,
ch.hasOwnProporty(‘fname’)    //false
ch.__proto__==fa.prototype    //false

通过上述代码大家可以看出来了吧,ch继承fa时只是让ch.__proto__指向了fa.prototype指向的地址,而不是指向fa.prototype。所以就导致了改写fa.prototype并不会影响ch.fname的值。改写后ch.__proto__不等于fa.prototype了,也就是说ch和fa已经半毛钱关系都没有了。
基本上到这里,大家都会对原型链有一定的认识了,至于对象中的特殊存在——function,大家可以自己去探索一下。以上都是自己对于JS中原型链的认识,如有错误欢迎大家指正~
ps:sf的编排好难用 !

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值