js中的原型继承

本文深入讲解JavaScript中的原型链机制,包括原型对象的概念、构造函数、原型属性及如何正确维护构造函数,确保原型链的完整性。

在谈Js之前,首先需要明确几个概念:

1.某一对象的原型仍是一个对象.

2.js原型继承的实质是复制,但也不是完全复制,而是子对象更改了什么数据,就复制什么数据.具体做法为创建一张成员维护表.如:

ExpandedBlockStart.gif 代码
function  Parent(b)  
 {  
     
this .a  =   " a " ;  
     
this .b  =  b == null   ?   " b "  : b;  
 };  
 
function  Child(b)  
 {  
     Parent.call(
this ,b);  
 };  
 Child.prototype 
=   new  Parent();  
 
var  c  =   new  Child();  
 alert(c.b)  

 

3.当需要查找某个属性时,首先读对象实例自己维护的表.如果没有找到指定的成员,就要遍历整个原型链,直到原型为空的对象({}),或者到找到该成员为止。

4.每个函数都有一个prototype属性指向其原型对象,默认为{},即空对象.

5.每个对象都有一个constructor属性,默认指向本对象的构造函数(这句话没有讲完,请接看往下看)

6.Objcet对象的prototype属性是空对象{},空对象的constructor属性指向Object函数构造,所以所有对象的总原型都是Object(参见第4条)

7.对于new操作符,比如:

 

function  Parent(b)  
 {  
     
this .a  =   " a " ;  
     
this .b  =  b == null   ?   " b "  : b;  
 };

对于:

var  p  =   new  Person( " c " );

其实可以写成这样:

var  p  =  {};  
Person.call(p,
" c " );  


 

这就很明白了,其实首先是创建了一个空对象,然后将这个对象以this参数传入构造函数Person,于是Person对象就new出来了.

好了,下面来说正题.上图:

 

 

 

 其实这是别人画的一张图,画的很好,我就拿来主义了,呵呵.代码如下:

ExpandedBlockStart.gif 代码
function  MyObject()   
 {   
     
this .constructor  =  arguments.callee;  // 正确维护constructor,以便回溯外部原型链   
 }   
 MyObject.prototype 
=   new  Object();  // 人为构建外部原型链   
  function  MyObjectEx()   
 {   
     
this .constructor  =  arguments.callee;  // 正确维护constructor,以便回溯外部原型链   
 }   
 MyObjectEx.prototype 
=   new  MyObject();  // 人为构建外部原型链   
 obj1  =   new  MyObjectEx();   
 obj2 
=   new  MyObjectEx();   
 alert(obj1.constructor 
===  MyObjectEx);  // true   
 alert(MyObjectEx.prototype  instanceof  MyObject);  // true   
 alert(MyObjectEx.prototype.constructor  ===  MyObject);  // true   
 alert(MyObject.prototype  instanceof  Object);  // true   
 alert(MyObject.prototype.constructor  ===  Object);  // true   
 alert(obj1.constructor.prototype.constructor.prototype.constructor  ===  Object);  // true,完成了所有的回溯  

 

学习JS的原型继承,最重要的就是搞清楚JS的原型链.

所谓原型链,就是一条记录了JS对象继承过程的链表,而其组成部分就是每个对象的constructor属性与函数的prototype属性. 通过对象的constructor属性可以找到本对象的构造构数,通过构造构数prototype属性又可以找到其上一级的对象.就这样一级一级的回溯, 直到Object.

还有一条隐藏的原型链,通过.proto属性维护,不过这是系统自行维护的,不需要人为参与,也不可见,虽然firefox浏览可以访问,但还是不推荐.

看似简单,但是陷阱很多,如下:

 

function  Parent(){};  
 
function  Child(){};  
 Child.prototype 
=   new  Parent();  
 
var  p  =   new  Parent();  
 
var  c  =   new  Child();  
 alert(p.constructor 
==  c.constructor)    // true 

 

p与c明明一个是基对象,一个是子对象,怎么会p.constructor == c.constructor呢?

当你alert(p.constructor),显示的是Person(),但当你alert(c.constructor)时,打印的还是Person(),这就奇怪了,c的constructor不是Child()吗?

原因就是第三行代码:

 

Child.prototype  =   new  Parent(); 

文章开始时的第5条说过:每个对象都有一个constructor属性,默认指向本对象的构造函数,其实这句话没有说完:每个对象的prototype对象的constructor属性默认也指向本构造函数,但是,当你改变了其prototype所指对象后,情况就有点不同了,如上例,当指向Parent 后,原型对象的constructor会指向Person,这是对的,然而,由于原型继承的本质是复制,会把这个属性值复制过来,会改变本对象的 constructor属性,让其也指向Person.

但是,如果代码这样写,结果就不同了:

 

function  Parent(){};  
 
function  Child(){};  
 
var  p  =   new  Parent();  
 
var  c  =   new  Child();  
 Child.prototype 
=   new  Parent();  
 alert(p.constructor 
==  c.constructor)    // false


 

 

我把它从第三行放到第五行.结果就是false了,原因是我先new对象,再更改原型.其实在对象被new出来之后,就与构造函数无关了,更改构造函数不会影响到已new出来的对象.

但是这样写的很少,因为这不符合面向对象的原则.回到刚才的话题,怎么才能在先设置原型再new对象的情况下让原型链记录正确呢?答案就是重新设置子对象的constructor属性:

 


 

function  Parent(){};  
 
function  Child()  
 {  
     
this .constructor  =  arguments.callee  
 };  
 Child.prototype 
=   new  Parent();  
 
var  p  =   new  Parent();  
 
var  c  =   new  Child(); 


 

 

通过这样的设置,就能达到那张图里所描绘的一条完整的原型链了.

参考的文章:

Javascript对象真经

http://w3er.com/blog/2009/03/master-javascript-object-system/#/2009/03/master-javascript-object-system/

悟透JavaScript

http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html

《悟透JavaScript》之 甘露模型(新)

http://www.cnblogs.com/leadzen/archive/2008/06/04/1213090.html

 


 

 

转载于:https://www.cnblogs.com/ljzforever/archive/2010/03/09/1681743.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值