博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解原型链
阅读量:5148 次
发布时间:2019-06-13

本文共 4071 字,大约阅读时间需要 13 分钟。

深入理解原型链

1.原型链

原型链作为实现继承的主要方法:其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

首先我们得清楚构造函数(constructor),原型对象(prototype)和实例的三者关系。

每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。所以当我们让一个构造函数的原型对象等于另一个类型的实例

function A(){};function B(){};B.prototype=new A();var instance=new B();

instance的原型对象是B.prototype,而B.prototype又等于构造函数A的一个实例对象,A的实例对象的原型对象是A.prototype,这样就形成了一条原型链了。

实例:

function SuperType(){//定义一个SuperType类型    this.property = true;//给实例对象添加一个属性}        SuperType.prototype.getSuperValue = function(){//给原型对象添加一个方法    return this.property;};        function SubType(){//定义一个SubType类型    this.subproperty = false;}        //inherit from SuperTypeSubType.prototype = new SuperType();//创建一个SuperType的实例对象,并把它赋值给SubType.prototype        SubType.prototype.getSubValue = function (){//给原型对象添加一个方法    return this.subproperty;//返回实例属性};var instance = new SubType();//创建一个对象实例alert(instance.getSuperValue());   //true

创建一个SuperType的实例对象,并把它赋值给SubType.prototype,也就是说,原来存在于SuperType的实例对象中的所有属性和方法,现在也存在于SubType.prototype中了,我们在给SubType.prototype添加了一个方法,这样就在继承SuperType的属性和方法的基础上又添加了一个新方法。

我们没有使用SubType默认提供的原型,而是给它换了一个新原型;这个新原型就是SuperType的实例。于是,新原型不仅具有作为一个SuperType的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SuperType的原型。

现在instance指向SubType的原型,Subtype的原型指向SuperType的原型。getSuperValue()方法仍然在SuperType.prototype中,但property则位于SubType.prototype中了。因为property是一个实例属性,而getSuperValue()方法则是一个原型方法。既然SubType.prototype现在是SuperType的实例,那么property当然就位于该实例中了。

注意:

instance.constructor此时指向的是SuperType,这是因为原来的SubType.prototype被重写的缘故。任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"SubType.prototype = new SuperType();"这一行,SubType.prototype.constructor是指向SubType的;加了这一行以后,SubType.prototype.constructor指向SuperType。更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。因此,在运行"SubType.prototype = new SuperType();"这一行之后,instance.constructor也指向SuperType!这显然会导致继承链的紊乱(instance明明是用构造函数SubType生成的),如果我们想要完全符合继承,可以将将SuperType.prototype对象的constructor值改为SuperType。

2.谨慎地定义方法

通过原型实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链

function SuperType(){    this.property = true;}        SuperType.prototype.getSuperValue = function(){    return this.property;};        function SubType(){    this.subproperty = false;}        //继承 SuperTypeSubType.prototype = new SuperType();        //使用字面量添加新方法,会导致上一行代码无效SubType.prototype = {    getSubValue : function (){        return this.subproperty;    },            someOtherMethod : function (){        return false;    }};        var instance = new SubType();alert(instance.getSuperValue());   //error!

3.原型链的问题

原型链虽然非常强大,但是它也存在一些问题,其中最大的问题就是:包含引用类型的原型属性会被所有实例共享。

而这也正是为什么要在构造函数中,而不是在原型对象定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就变成了现在的原型属性了。

function SuperType(){    this.colors = ["red", "blue", "green"];}function SubType(){            }        //inherit from SuperTypeSubType.prototype = new SuperType();var instance1 = new SubType();instance1.colors.push("black");alert(instance1.colors);    //"red,blue,green,black"        var instance2 = new SubType();alert(instance2.colors);    //"red,blue,green,black"

SuperType定义了一个colors的实例对象属性,该属性包含一个数组(引用类型值),SuperType的每个对实例都会有自己的colors属性。当SubType通过原型链继承了SuperType之后,SubType.prototype就成了SuperType的一个实例,因此它也拥有了一个它自己的colors属性,就更专门创建了SubType.prototype.colors属性一样。也正因为如此,所以SubType构造函数的实例对象都会通过原型链继承这个属性,当其中某个实例对象改变了这个属性,在其他实例中也会体现出来。

在看看下面这种情况

function SuperType(){    this.colors = ["red", "blue", "green"];}function SubType(){            }        //inherit from SuperTypeSubType.prototype = new SuperType();var instance1 = new SubType();instance1.colors= ["blue", "green"];//重写了colors属性,现在这个属性是属于instance1自己的,也就是不再是对原型对象上的属性引用了alert(instance1.colors);    //"blue,green"        var instance2 = new SubType();alert(instance2.colors);    //"red,blue,green"//引用(继承)原型对象上的属性,实际属性还是部署在原型对象上的。

这个实例与前面的实例有什么区别:colors都是原型属性,所以每个实例对象都会继承这个属性,如果没有重写这个属性,那么实例对象的这个属性都是对原型对象的该属性的引用,改变它会反映到其他实例对象中,如果重写了,那么这个属性就会覆盖原来的属性,从而变成自身的属性。

4.确定原型和实例的关系的方法:

(1)通过instanceof

alert(instance instanceof Object) //true

(2)通过isPrototypeof

alert(Object.prototype.isPrototypeof(instance)); //true

参考

(1)JavaScript高级程序设计第六章

(2)

转载于:https://www.cnblogs.com/YeChing/p/6347009.html

你可能感兴趣的文章
apn描述文件下载_iOS 13描述文件放出 可以提前尝鲜啦!(附下载)
查看>>
win7更新错误0x800b0109_幺蛾子真多!Windows更新又闯祸了
查看>>
工业机器人什么情况下会出现奇点_汽车为什么会烧机油?车辆在什么情况下会出现烧机油的情况?...
查看>>
vant 带关闭按钮div_小米升级MIUI 11后,请先调整这3个按钮,不然电池会变得很不耐用...
查看>>
泰坦机器人节目秀_请自觉遵守规章制度,巡逻机器人正在执勤!
查看>>
手机闪存速度排行_你的手机在里面吗?目前几款主流处理器排行
查看>>
私有方法不能被子类覆盖_Java核心技术,继承:类+继承层次+强制类型转换+多态+抽象类...
查看>>
冯珊珊_专访一姐冯珊珊(上):高尔夫有助于K12教育 进社区将是趋势
查看>>
删除注册表文件_为了保护个人隐私,用这4招删除win10中的搜索历史记录
查看>>
3控制ie文档模式_Chromium版Edge浏览器将支持多平台,Windows版支持IE模式
查看>>
右键新建文件夹_Win10右键菜单、资管管理器整理
查看>>
接口有个电池标志_【创新+】206所在“脑机接口”及燃料电池领域分别获批国家自然科学基金项目...
查看>>
初中节点法分析电路_初三物理电路图解题思路:电路简化原理
查看>>
solr7集群 springboot_Springboot2(44)集成solr7,solrCloud
查看>>
python判断循环语句_Python之循环判断语句
查看>>
python 列表推导式 else_列表推导式与表达式生成器在 Python 中的滥用!
查看>>
python语言的关键字是什么_Python语言的关键字有哪些特点?关键字列举
查看>>
x3650m5不自动进系统_解决x3650m5卡在开机Optimized Boot界面
查看>>
mysql 类型_MySQL 数据类型有哪些?
查看>>
js 循环 等待异步执行完再执行_JS的同异步执行顺序分享给大家
查看>>