js中的继承

来源:互联网 发布:研究生做软件测试待遇 编辑:程序博客网 时间:2024/06/05 17:55

 js的继承和诸如C#、java等面向对象的继承在原理上有很大的差别:js的继承借助this关键字和原型对象prototype实现。

对象冒充:

  借助this关键字,给所有的属性、方法赋值,也就是采用类声明的构造函数方式。因为构造函数只是一个函数,所以,可以使ClassA的构造函数,成为ClassB的一个方法,然后调用它,这样一来,ClassB就收到了ClassA的构造函数中定义的属性和方法。

[javascript] view plain copy
  1. <span style="font-size:18px;">对象冒充实现继承:  
  2. function ClassA(sColor){  
  3.     this.color=sColor;  
  4.     this.sayColor=function(){  
  5.         alert(this.color);  
  6.     }  
  7.   
  8. }  
  9.   
  10. function ClassB(sColor,sName){  
  11.   
  12.     this.newMethod=ClassA;//为ClassA赋予方法newMethod(函数名只指向它的指针),这里的ClassA是作为常规的函数来建立基础机制,而不是作为构造函数,也可以理解成,将构造函数当做常规函数来处理。  
  13.     this.newMethod(sColor);//调用newMethod方法,传递的是ClassB的构造参数sColor  
  14.     delete this.newMethod;//删除对ClassA的引用后,就不再调用它,所有的新属性和方法都必须在删除了新方法后定义,否则可能会覆盖超类的相关属性和方法。  
  15.       
  16.     this.name=sName;  
  17.     this.sayName=function(){  
  18.         alert(this.name);  
  19.     }  
  20. }  
  21.   
  22. //调用  
  23. var objA= new ClassA("red");  
  24. var objB = new ClassB("blue","Nicholas");  
  25. objA.sayColor();//red  
  26. objB.sayColor();//blue  
  27. objB.sayName();//Nicholas  
  28. </span>  


  对象冒充是可以实现多继承的:

  例如,ClassZ要继承ClassX和ClassY: 

[javascript] view plain copy
  1. <span style="font-size:18px;">ClassZ继承ClassX和ClassY  
  2. function ClassZ(){  
  3.     this.newMethod=ClassX;  
  4.     this.newMethod();  
  5.     delete this.newMethod;  
  6.       
  7.     this.newMethod=ClassY;  
  8.     this.newMethod();  
  9.     delete this.newMethod;  
  10. }</span>  

  这里有个弊端,如果ClassX和ClassY具有相同的属性和方法,ClassY具有高优先级,因为它是从后面的类继承。

  其实上面的这些方法只为更好的理解一下js中的继承的实现思路,而js中经常使用的是call和apply方法
  他们的第一个参数都用作this的对象,其他参数传递给函数自身:call方法中是逐个的参数,apply是参数数组。

[javascript] view plain copy
  1. <span style="font-size:18px;">function ClassB(sColor,sName){  
  2.     //this.newMethod=ClassA;  
  3.     //this.newMethod(sColor);  
  4.     //delete this.newMethod;  
  5.       
  6.     ClassA.call(this,sColor);       //与上面的直接使用对象冒充是等效的  
  7.     //ClassA.apply(this,new Array(sColor)); //如果是apply,第二个参数则是数组  
  8.     //ClassA.apply(this,arguments);     //使用js的内部对象arguments  
  9.       
  10.   
  11.   
  12.     this.name=sName;  
  13.     this.sayName=function(){  
  14.         alert(this.name);  
  15.     };  
  16. }  
  17.   
  18.   
  19. </span>  

  call的一个单独示例:

[javascript] view plain copy
  1. <span style="font-size:18px;">function sayColor(sPrefix,sSuffix){  
  2.     alert(sPrefix+this.color+sSuffix);  
  3. };  
  4.   
  5. var obj= new Object();  
  6. obj.color="red";  
  7.   
  8. sayColor.call(obj,"The color is","a very nice color indeed");</span>  


  apply的一个单独示例:

[javascript] view plain copy
  1. <span style="font-size:18px;">function sayColor(sPrefix,sSuffix){  
  2.     alert(sprefix+this.color+sSuffix);  
  3. };  
  4. var obj = new Object();  
  5. obj.color="red";  
  6. //将一个方法绑定到一个对象上  
  7. sayColor.apply(obj,new Array("The color is","a very nice color indeed"));</span>  


  在js中会大量用到call和apply,使用的过程中,也会有些注意的地方,这里先不做赘述。这里我们只需理解:call和apply函数都可以将一个函数绑定到一个对象上,或者是在子对象中继承父类的方法。两者的差异是给调用的函数,传递参数的方式不同(call是参数,apply是参数数组,且往往和arguments配合使用,需要父类和子类的参数顺序完全相同,否则需要重新定义一个数组)。

说到js的继承,必须要提的是prototype原型链:

  prototype对象相当于一个模板,要实例化的对象都会以这个模板为基础,那么,在prototype模板中定义的属性和方法,自然会被继承到它的实例中,这就是原型链的原理。

  针对上面的例子,使用原型链实现继承:  

[javascript] view plain copy
  1. <span style="font-size:18px;">function ClassA(){ }  
  2. ClassA.prototype.color="red";  
  3. ClassA.prototype.sayColor=function(){  
  4.     alert(this.color);  
  5. };  
  6.   
  7. function ClassB(){  
  8.       
  9. }  
  10. //原型链中标准的做法是在调用ClassA的构造函数的时候没有任何参数  
  11. ClassB.prototype= new ClassA();  
  12. ClassB.prototype.name="";  
  13. ClassB.prototype.sayName=function(){  
  14.     alert(this.name);  
  15. };  
  16.   
  17. 调用:  
  18. var objA= new ClassA();  
  19. var objB= new Classb();  
  20. objB.color="red";  
  21. objA.color="blue";  
  22. objB.name="Nicholas";  
  23. objA.sayColor();//red  
  24. objB.sayColor();//blue  
  25. objB.sayName();//Nicholas  
  26.   
  27. 有意思的是再原型链中,instanceof 运算符的运算方式也很独特,对ClassB的所有实例,instanceof 为ClassA和ClassB都返回true  
  28.   
  29. var objB= new ClassB();  
  30. alert(objB instanceof ClassA);//true  
  31. alert(objB instanceof ClassB);//true</span>  


 使用prototype需要注意的几个地方:

1、原型链中标准的做法是在调用ClassA的构造函数的时候没有任何参数。

2、instanceof 运算符的运算方式也很独特,对ClassB的所有实例,instanceof 为ClassA和ClassB都返回true。

3、使用原型链的方式,可以批量的将ClassA的属性和方法都赋予给ClassB,而prototype对象的作用就是封装这些参数。

4、子类中所有的属性和方法都需要在prototype属性被赋值后,因为,如果在它赋值前定义,那么后面再次赋值之后都会被替换掉。

5、使用原型链的一个弊端是,不支持多重继承,另外会用父类的的对象重写子类的原型链的prototype属性。

综上:对象冒充的弊端是必须使用构造函数方式,具有局限性;使用原型就无法使用带参数的构造函数了,那么解决这个问题呢?

 答案是:采用构造函数的方式定义属性,采用原型的方式定义方法。

[javascript] view plain copy
  1. <span style="font-size:18px;">采用构造函数定义属性,原型的方法定义方法:  
  2. function ClassA(sColor){  
  3.     this.color=sColor;  
  4. }  
  5.   
  6. ClassA.prototype.sayColor=function(){  
  7.     alert(this.color);  
  8. }  
  9.   
  10. function ClassB(sColor,sName){  
  11.     //ClassA的call方法,对象冒充继承ClassA类的sColor属性。  
  12.     ClassA.call(this,sColor);  
  13.     this.name=sName;  
  14. }  
  15.   
  16. ClassB.prototype= new ClassA();//使用原型链的方式,继承ClassA的方法(构造函数没有参数),因为使用了prototype,所以,instanceof运算符仍能正确的运行。  
  17.   
  18.   
  19. ClassB.prototype.sayName=function(){  
  20.     alert(this.name);  
  21. };  
  22.   
  23.   
  24. 调用:  
  25. var objA = new ClassA("red");  
  26. var objB = new ClassB("blue","Nicholas");  
  27.   
  28. objA.sayColor();//red  
  29. bojB.sayColor();//blue  
  30. bojB.sayName();//Nicholas  
  31. </span>  

  这里的继承由ClassA.call(this,sColor)和ClassB.prototype= new ClassA()实现。
  使用原型链的方式,继承ClassA的方法(构造函数没有参数),因为使用了prototype,所以,instanceof运算符仍能正确的运行。
  ClassA的call方法,对象冒充继承ClassA类的sColor属性。

   经过一些列的演变,找到了js中最优秀的继承继承机制:采用构造函数的方式定义属性,采用原型的方式定义方法。这样在子类的定义中,既可以使用带参数的构造函数,也可以使用prototype批量的继承父类的方法。

  另外,这里使用到了js中两个常用的方法:call和apply。他们不仅可以在类的继承中发挥很大的作用,还在将一个方法捆绑到另一个对象的时候经常使用。

  使用call和apply的区别在于:参数的传递方法(call传递的是逐个的参数,apply传递的是一个数组,很多时候配合arguments使用,但是要求继承的父类和子类的函数的参数顺序是一样的)。

  这两个方法在使用的时候,还需要注意的地方,另外js中还有其他扩展或者捆绑的函数observer() bindAsEventListener(),也可以重新定义bindAsEventListener的绑定方法,将在后续的文章中深入学习。


0 0
原创粉丝点击