UDN-企业互联网技术人气社区
UDN 企业互联网技术社区 前端精选 JavaScript精选 JavaScript 原型链维护和继承详解

JavaScript 原型链维护和继承详解

刘莉莉11 论坛 2016-5-3 14:14
分享到:
摘要: 面向对象中继承有一个特性:相似性.子类与父类具有相似性.因此在子类中你是无法用delete删除从父类继承而来的成员.也就是说子类必须具有父类的特性.
        本文通过实例详细向我们分析了JavaScript原型链维护和继承的问题,十分的详尽,十分的全面,这里推荐给大家。
  
  一.两个原型
  
  很多人都知道JavaScript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把JavaScript的继承演义的美轮美奂了.
  
  其实啊,光靠这一个属性是无法完成JavaScript的继承.
  
  我们在代码中使用的prototype完成继承在这里就不多说了.大家可以查一下资料.
  
  另外一个看不见的prototype成员.
  
  每一个实例都有有一条指向原型的prototype属性,这个属性是无法被访问到的,当然也就无法被修改了,因为这是维护JavaScript继承的基础.
  
  复制代码 代码如下:

  1. //构造器声明
  2.         function Guoyansi(){ }
  3.         function GuoyansiEx(){}
  4.         //原型继承
  5.          GuoyansiEx.prototype=new Guoyansi();
  6.        //创建对象
  7.        var g1=new GuoyansiEx();
  8.        var g2=new GuoyansiEx();
复制代码
  
  上面的代码中的对象可以用下面的图来说明

 
201411260910101.jpg
 

  二.原型的维护
  
  一个构造器产生的实例,其constructor属性总是指向该构造器.我们暂且认为该话是对的.
  
  复制代码 代码如下:
  

  1. function Guoyansi(){ }
  2. var obj1=new Guoyansi();
  3. console.log(obj1.constructor===Guoyansi);//true
复制代码

  其实构造器本身是没有constructor这个属性的,那么这个属性是来自哪呢?
  
  答案是:来自原型.
  
  因此得出下面的结论
  
  复制代码 代码如下: 
  1.   obj1.constructor===Guoyansi.prototype.constructor===Guoyansi
复制代码
  
  既然我们可以通过constructor来寻找构造器.因此我们就可以进一步完善上面的图了.

201411260910102.jpg
 
 
  复制代码 代码如下:

  1. function GuoyansiEx(){}
  2.              GuoyansiEx.prototype=new Guoyansi();
  3.              console.log(GuoyansiEx.constructor===GuoyansiEx)//false  
复制代码

  根据上图,上面的结果应该是true,但为什么是false呢?
  
  现在做个分析.
  
  GuoyansiEx的原型被Guoyansi的实例重写了,那么GuoyansiEx的原型中的constructor自然也是来自Guoyansi的实例.
  
  而Guoyansi实例中的constructor又是来自Guoyansi.prototype.而Guoyansi.prototype没有被重写,
  
  所以Guoyansi.prototype的constructor指向Guoyansi(构造函数);
  
  根据以上分析得出下面的结论
  
  复制代码 代码如下:
  
  
  1. GuoyansiEx.constructor===Guoyansi.constructor===Guoyansi;
复制代码

  
  如果在开发过程中对于Constructor的指向要求非常精确的话,可以做如下处理.
  
  复制代码 代码如下:

  1. /**方法一:**/
  2. function Guoyansi(){}
  3.              function GuoyansiEx(){}
  4.              GuoyansiEx.prototype=new Guoyansi();
  5.              GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
复制代码
 
  复制代码 代码如下:

  1. /**
  2.             方法二
  3.             **/
  4.             function Guoyansi(){}
  5.             function GuoyansiEx(){
  6.                 this.constructor=arguments.callee;
  7.             }
  8.             GuoyansiEx.prototype=new Guoyansi();
复制代码
  
  复制代码 代码如下:

  1. /**
  2.             方法三
  3.             **/
  4.             function Guoyansi(){}
  5.             function GuoyansiEx(){
  6.                 this.constructor=GuoyansiEx;
  7.             }
  8.             GuoyansiEx.prototype=new Guoyansi();
复制代码
 
  三.看不见的原型有什么用呢?
  
  看得见的原型链我们可以对他操作来完成我们的继承,那么这个看不见的原型链我们既看不见,又无法操作.要它有何用.
  
  面向对象中继承有一个特性:相似性.子类与父类具有相似性.因此在子类中你是无法用delete删除从父类继承而来的成员.也就是说子类必须具有父类的特性.
  
  为了维护这个特性,JavaScript在对象的内部产生了一条我们看不见的原型属性,并且不允许用户访问.这样,用户可以处于任何目的来修改constructor,
  
  而不会破坏子类拥有父类的特性.
  
  简而言之:内部原型是JavaScript的原型继承机制所需要的,而外部原型是用户实现继承所需要的.
  
  四.火狐引擎SpiderMonkey中的__proto__
  
  还是这段代码.
  
  复制代码 代码如下:

  1. function Guoyansi(){}
  2.             Guoyansi.prototype.age=24;
  3.             function GuoyansiEx(){}
  4.             var obj1=new Guoyansi();
  5.             GuoyansiEx.prototype=obj1;
  6.             GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
  7.             var obj2=new GuoyansiEx();
复制代码
  
  我现在想要从obj开始向上访问父类Guoyansi的prototype的属性的age.
  
  思路是这样的.
  
  第一步:obj2====>obj2.constructor.prototype
  
  第二部:obj2.constructor.prototype===>GuoyansiEx.prototype;
  
  第三部:GuoyansiEx.prototype===>obj1;
  
  第四部:obj1.constructor====>Guoyansi
  
  第五部:Guoyansi.prototype.age
  
  写成这这样:console.log(obj2.constructor.prototype.constructor.prototype.age)//24;
  
  最终的结果是24.
  
  最终的结果是24.可以正常执行,但是在好多书上说constructor修改后,级无法在找到父类中的原型了.不知道是怎么回事.
  
  在火狐中提够了一种更加简洁的属性._proto_
  
  SpiderMonkey中默认在任何创建的对象上添加了一个名为_proto_的属性,该属性指向构造器所用的原型.
  
  其实就是我们上面提到的不可见的原型链,只不过是在这个地方变相的公开而已.
  
  可以这样访问到age
  
  console.log(obj2.__proto__.__proto__.age);//24
  
  这样的确是成功的访问到了父类的原型属性,但是这个属性只适用于火狐,在其他浏览器中是会出错的.
  
  在E5中对Object做出了扩展Object.getPrototypeOf(),可以访问到所有父类的原型了.
  
  复制代码 代码如下:

  1. function Guoyansi(){}
  2.             Guoyansi.prototype.age=24;
  3.             function GuoyansiEx(){}
  4.             var obj1=new Guoyansi();
  5.             GuoyansiEx.prototype=obj1;
  6.             GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
  7.             var obj2=new GuoyansiEx();
  8.             var proto=Object.getPrototypeOf(obj2);
  9.             while(proto){
  10.                 console.log(proto.constructor);
  11.                 proto=Object.getPrototypeOf(proto);
  12.             }
  13.             console.log("object的原型"+proto);
复制代码
  
  结果是:GuoyansiEx
  
  Guoyansi
  
  Object
  
  object的原型null
  
  个人觉得这些应该算是JavaScript面向对象的精髓之一了.小伙伴们自己参考下,根据需求使用到自己的项目中去吧

相关阅读

分享到:
已有2条评论

最新评论

不帅
路过 帮顶 嘿嘿
猩猩点灯
OMG!介是啥东东

一周焦点

关注我们:
关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
返回顶部