JavaScript 的继承方式与取舍

来源:互联网 发布:医疗软件销售招聘 编辑:程序博客网 时间:2024/06/05 02:12

(序语:本人写得不是很好,如果有什么错误请指出或者联系我e-mail:kenlist@163.com)

Javascript中的继承方式主要有3种.之所以会有多种继承方式,是因为ECMAScript制造者当初没有真正为继承定义一套完整的规定.事实上,这里的继承方式都是开发者们通过努力获得的.

第一种(call继承法):

使用call关键字,是调用函数(function对象)的函数.

例如:

function sayColor(sPrefix,sSuffix){document.write(sPrefix + this.color + sSuffix);}

var obj = new Object();

obj.color 
=  "red";

sayColor.call(obj,
"the color is ",",a very nice coor indeed.");

 

这里输出了the color is red,a very nice coor indeed.从而可以知道它能把内部的this指针指向输入对象(即obj).

深入了解call,使我们一会儿对继承使用有更好了解.现在我们再看看下面的代码:

 

function sayColor(sPrefix,sSuffix){

document.write(sPrefix 
+ this.color + sSuffix);

this.showgood = function(){document.write("hello.");};

this.color = "blue";

}


var obj = new Object();

obj.color 
=  "red";

sayColor.call(obj,
"the color is ",",a very nice color indeed.");

obj.showgood();

document.write(obj.color);

输出:this color is red,a very nice color indeed.hello.blue

由输出见,因为sayColor内部的this指针通过call函数调用时,指向了obj,所以obj就绑定了showgood()和color.而因为obj原来就有color一值,当调用call时,此值被重新赋予指针,即this.color覆盖原来的obj.color,所以值发生改变.由此可见,call函数并不单纯地调用函数,它还能把内部的this指向我们输入的对象.从而,聪明的开发者们想到了用它来作继承.(这种方法又称对象冒充法)下面代码为继承代码:

 

function ClassA(sColor){

this.color = sColor;

this.showColor = function(){ document.write(this.color);};

}


function ClassB(sColor,sName){

ClassA.call(
this,sColor);  //这里就使用call方法,使ClassA的属性方法绑定到这里.

this.name = sName;

this.showname = function(){document.write(sName))};

}


var objA = new ClassA("red");

objA.showColor();

var objB = new ClassB("blue","kenlist");

objB.showColor();

objB.showName();

document.write(objB 
instanceof ClassA);  //返回false

从输出可以知道,的确成功地继承了并重新赋值了.并且在最后我家了一句instanceof的比较语句, 也成功地返回了false.

这种继承方法有个优点,就是能执行多继承,可以同时绑定多个类.但由于call函数的特性,最下面的类的方法总是拥有优先级.

这种继承方式也有个缺点,就是不能继承原型模版的方法与属性,例如如果把ClassA中的this.color改为ClassA.prototype.color则会输出undefined.为什么会这样?因为call是把ClassB对象给予ClassA去进行指针定向,由前面知识可以知道,如果不用this的话,是没办法让指针指到ClassB类上的,所以prototype只是ClassA的模版,并不能使里面的方法属性指向ClassB.但由前面类的知识可知,我们创建类时,虽然要原型来定义方法,这样才能使类不重复分配一个空间.很显然,这种继承方法存在这种缺陷.下面将讲解第二种方法去继承,这种方法从一定程度上祢补了这个缺陷.

第二种(原型链继承):

先看代码:

function ClassA(){
      ClassA.prototype.color 
= "red";
               ClassA.prototype.sayColor 
= function(){
                         document.write(
"my color is:" + this.color + "<br/>");
                 }
;
       }

   
   
function ClassB()
    
this.name = "kenlist";
    ClassB.prototype.sayName 
= function(){
              document.write(
"my name is:" + this.name);
     }
;
   }

   
   
//使用原型链覆盖到ClassB的原形模版上.
   //不能使用构造函数.因为这里的new ClassA()括号内不允许有任何东西,这是它的定义.
   ClassB.prototype = new ClassA();
   
   
var objA = new ClassA();
   
var objB = new ClassB();
   objA.sayColor();
   objB.sayColor();
   
//因为模版复制问题,这里会返回true
   document.write(objB instanceof ClassA);

 

输出是正常地输出,从代码中可以看出,关键部分就是使用了ClassB.prototype = new ClassA()这里,这里的意思为把ClassA的原型模版先用new语句复制一个新的出来,然后把ClassB的原型模版指针指向复制品.从而导致ClassB具有了ClassA的模版.这个语句不能放在建立类的内部,因为在function Classb()内创建的只能是基于一个特定的模版进行修改,而不能在里面重新定义原型模版.所以必须在外面先建立原型模版.然后再在ClassB里面继续添加属性与方法.

这种方法缺点非常多,我们一条条地列出:

1)不能使用构造函数,在其中一条蓝色字的注释已经说明,当让ClassB的原型链指向ClassA的原型链时,不能在ClassA()的括号中添加任何参数,这导致了不能为ClassA输入参书,从而导致缺少了构造函数.

2)基类一定要用原型模版定义所有方法和属性,否则没法继承.因为我们定义类时,属性通常是绑定到该对象上,所以使用this指针,然而这里如果使用this指针则没办法由原型模版实现继承.

3)在橙色字的注释中可以知道,objB instanceof Classa竟然是返回true,事实上,instanceof语句检测的是原型模版的指向性,因为ClassB实际上是指向ClassA的模版,所以instanceof语句就分不清ClassB和ClassA了.

4)不能实现多继承,由于原型模版的重定向会使原来的模版丢失,所以不能实现多继承.

基于以上2种方法的缺陷,下面的第三种方法,则能则是参照类的做法.

第三种(混合对象冒充/原型链继承)

由前面2种方法知道可以通过call使this绑定到子类上,通过原型链继承,可以把原型模版复制到子类.这两种方法结合,就能使混合方式的类得到较好的继承.看看下面代码:

function ClassA(sColor){

this.color = sColor; //属性使用对象绑定

ClassA.prototype.showColor 
= function(){ document.write(this.color); }//方法使用原型模版法

}


function ClassB(sColor,sName){

ClassA.call(
this,sColor); //使属性绑定到该类

this.name = sName;

ClassB.prototype.showName 
= function(){ document.write(this.name);};

}


ClassB.prototype 
= new ClassA();  //使用原型链方法使复制模版

var objA = new ClassA("red");

objA.showColor();

var objB = new ClassB("blue","kenlist");

objB.showColor(); objB.showName();

 输出结果很正常,而且也实现了属性使用绑定,方法使用原型模版.这种方法也祢补了上面2种继承方法的缺陷.从而达到较好的继承效果.但这种方法也是有缺陷,因为使用原型模版重定向方法继承,从而导致不能多继承.而且因为原形模版重定向不能写在类内部,也不能是用动态原型方法,从而浪费了比小内存.荣幸的是,聪明的开发者们开发了个小库来帮助我们完成这些任务,祢补这些缺失.

第四种(zinherit继承)

这种方法是最有效,最规范,缺失最小的一种,但它需要从网上下载一个小库zinherit.js(http://www.nczonline.net/downloads处下载)这个小库能有效解决多重继承和动态原型问题.看看下面代码:

 

 function ClassA(sColor){
            
this.color = sColor;
            
if(typeof ClassA._initialized == "undefined"){
                     ClassA.prototype.showColor 
= function(){
                                document.write(
this.color + "<br/>");
                      }
 ;
                     ClassA._initialized 
= true;
             }
;
         }

  
   
function ClassB(sName){
    
this.name = sName;
             
if(typeof ClassB._initialized == "undefined"){
                       ClassB.prototype.showName 
= function(){
                                 document.write(
this.name + "<br/>");
                        }
;
                       ClassB._initialized 
= true;
             }
;
   }

   
   
function ClassC(sColor,sName,sSize){
             
this.size = sSize;
    
             
//先使用call方法是属性绑定
             ClassA.call(this,sColor);
             ClassB.call(
this,sName);
             
//下面使用小库zinherit.js的原型模版继承方法
             //这种继承方法使原型模版重定向能在类内部完成.
    
             
if(typeof ClassC._initialized == "undefined"){
                       ClassC.prototype.inheritFrom(ClassA);  
                       ClassC.prototype.inheritFrom(ClassB);
                       ClassC.prototype.showSize 
= function(){
                                 document.write(
this.size + "<br/>");
                       }
;
              ClassC._initialized 
= true;
             }
;
   }
 
   
   document.write(
"-----ClassA-----<br/>");
   
var objA = new ClassA("red");
   objA.showColor();
   document.write(
"-----ClassB-----<br/>");
   
var objB = new ClassB("superman");
   objB.showName();
   document.write(
"-----ClassC-----<br/>");
   
var objC = new ClassC("orange","kenlist",100);
   objC.showColor();
   objC.showName();
   objC.showSize();

输出:

-----ClassA-----
       red
       -----ClassB-----
       superman
       -----ClassC-----
       orange
       kenlist
       100

由输出可以得出,这种建立类的方法非常符合标准,只需要在类中加入继承语句:ClassC.prototype.inheritForm(ClassB),就能使ClassC的原型模版得到重定向并且不用定义在类外面.还能实现多继承,动态原型.

分析了上面4种方法(call继承、原型链继承、混合对象冒充/原型链继承、zinherit继承),我觉得第4种方法比较有效,但要引入库而且通过库处理会使执行效率降低,所以如果不需要多继承,建议使用混合对象冒充/原型链继承方法。但如果对执行速率要求不是太高或者要求多继承,则建议使用zinherit继承方法,因为这种方法既能规范化,又能实现多继承。

写了这么多,可能存在不小勘误,希望大家多给意见哈~

 
原创粉丝点击