javascript的繼承實現

javascript雖然是一門面向對象的語言,但是它的繼承機制從一開始設計的時候就不同於傳統的其他面嚮對象語言,是基於原型的繼承機制,但是在這種機制下,繼承依然有一些不同的實現方式。

方法一:類式繼承

所謂的類式繼承就是指模仿傳統面嚮對象語言的繼承方式,繼承與被繼承的雙方都是“類”,代碼如下:

首先定義一個父類(或超類):

複製代碼

function Person(name){        this.name=name;
    }

    Person.prototype.getName=function(){        return this.name;
    };

複製代碼

該父類person的屬性在構造函數中定義,可以保證繼承它的子類的name屬性不與它共享這個屬性,而是單獨屬於子類,將getName方法掛載 到原型上,是爲了讓繼承它的子類的多個實例共享這一個方法體,這樣能夠節省內存,(對於多個實例來講,每次New一個實例出來都會保證這些實例的 getName方法引用的是同一段內存空間,而不是獨立的空間)。

定義一個繼承方法extend,如下:

複製代碼

function extend(subClass,superClass){        var F=function(){};
        F.prototype=superClass.prototype;
        subClass.prototype=new F();
        subClass.prototype.constructor=subClass;

        subClass.superClass=superClass.prototype;        if(superClass.prototype.constructor==Object.prototype.constructor){
            superClass.prototype.constructor=superClass;
        }

    }

複製代碼

在這個方法中,首先創建一個新的類爲F,讓它的原型爲父類的原型,並且讓子類的原型指向該類F的一個實例,從而達到了一個繼承父類的目的,同時,子 類的原型由於被修改,所以將修改後的原型的constructor屬性指向子類,讓其擁有構造函數,同時給該子類掛載一個superClass屬性,子類 可以通過該屬性調用父類,從而建立起了子類和父類的關係。

定義一個子類Author來繼承父類Person,如下:

複製代碼

function Author(name,books){
       Author.superClass.constructor.call(this,name);       this.book=books;
   }
   extend(Author,Person);
    Author.prototype.getBooks=function(){        return this.book;
    }

複製代碼

這裏在子類的構造函數中通過其superClass屬性調用父類的構造函數,同時採用call方法,轉換該方法調用的this指向,讓子類 Author也擁有(繼承)父類的屬性,同時子類又擁有自己的屬性book,所以在構造函數中將參數books賦值給屬性book,達到構造的目的。採用 extend函數繼承父類Person原型上的屬性和方法(實際上只繼承了方法,因爲我們之前只是將方法掛載到了原型上,屬性是在構造函數中定義的)。同 時,Author又擁有自己的方法getBooks,將其掛載到對應的原型上,達到了在繼承的基礎上進一步擴充自身的目的。

這種繼承方式很明顯就是採用類似於傳統面嚮對象語言的類式繼承,優點是對習慣於傳統面向對象概念的程序員來講很容易理解,缺點是過程比較繁瑣,內存 消耗稍大,因爲子類也擁有自己的構造函數及原型,而且子類和父類的屬性完全是隔離的,即使兩者是一樣的值,但是不能共享同一段內存。

方法二:原型式繼承

首先定義一個父類,這裏不會刻意模仿使用構造函數來定義,而是直接採用對象字面量的方式定義一個對象,該對象就是父類

複製代碼

var Person={
      name:'default name',
      getName:function(){          return this.name;
      }

  }  ;

複製代碼

和第一種方法一樣,該對象擁有一個屬性name和一個方法getName .

然後再定義一個克隆方法,用來實現子類對父類的繼承,如下:

function clone(obj){
         function F(){}
         F.prototype=obj;         return new F();
     }

該克隆方法新建一個對象,將該對象的原型指向父類即參數obj,同時返回這個對象。

最後子類再通過克隆函數繼承父類,如下:

 var Author=clone(Person);
   Author.book=['javascript'];
   Author.showBook=function(){
   return this.book;
 }

這裏定義一個子類,通過clone函數繼承父類Person,同時拓展了一個屬性book,和一個方法showBook,這裏該子類也擁有屬性name,但是它和父類的name值是一樣的,所以沒有進行覆蓋,如果不一樣,可以採用

 Author.name='new name';覆蓋這個屬性,從而得到子類的一個新的name屬性值。

這種原型式繼承相比於類式繼承更爲簡單自然,同時如果子類的屬性和父類屬性值相同,可以不進行修改的話,那麼它們兩者其實共享的是同一段內存空間, 如上面的name屬性,缺點是對於習慣了傳統面向對象的程序員難以理解,如果兩者要進行選擇的話,無疑是這種方式更爲優秀一些。

既然javascript中採用基於原型的方式來實現繼承,而且每個對象的原型只能指向某個特定的類的實例(不能指向多個實例),那麼如何實現多重繼承(即讓一個類同時具有多個類的方法和屬性,而且本身內部不自己定義這些方法和屬性)?

在javascript設計模式中給出了一種摻元類(mixin class)的方式:

首先定義一個摻元類,用來保存一些常用的方法和屬性,這些方法和屬性可以通過拓展的方式添加到任何其他類上,從而被添加類就具有了該類的某些方法和屬性,如果定義多個摻元類,同時添加給一個類,那麼該類就是間接實現了“多重繼承”,基於這種思想,實現如下:

摻元類定義:

複製代碼

var Mixin=function(){};
    Mixin.prototype={
        serialize:function(){            var output=[];            for(key in this){
                output.push(key+":"+this[key]);
            }            return output.join(',');
        }
    }

複製代碼

該摻元類具有一個serialize方法,用來遍歷其自身,輸出自身的屬性和屬性值,並且將他們以字符串形式返回,中間用逗號隔開。

定義一個擴充方法,用來使某個類經過擴充之後具備摻元類的屬性或方法,如下:

複製代碼

function augment(receivingClass,givingClass){       if(arguments[2]){           for(var i= 2,len=arguments.length;i<len;i++){
               receivingClass.prototype[arguments[i]]=givingClass.prototype[arguments[i]];
           }
       }       else
       {           for(methodName in givingClass.prototype){               if(!receivingClass.prototype[methodName]){
                   receivingClass.prototype[methodName]=givingClass.prototype[methodName];
               }
           }

       }
  }

複製代碼

該方法默認是兩個參數,第一個參數是接受被擴充的類,第二個參數是摻元類(用來擴充其他類的類),還可以有其他參數,如果大於兩個參數,後面的參數 都是方法或者屬性名,用來表示被擴充類只想繼承摻元類的指定的屬性或方法,否則默認繼承摻元類的所有屬性和方法,在該函數中,第一個if分支是用來繼承指 定屬性和方法的情形,else分支是默認繼承所有屬性和方法的情形。該方法的實質是將摻元類的原型上的屬性和方法都擴充(添加)到了被擴充類的原型上面, 從而使其具有摻元類的屬性和方法。

最後,使用擴充方法實現多重繼承

  augment(Author,Mixin);     var author= new Author('js',['javascript design patterns']);
     alert(author.serialize());

這裏定義了一個author的類,該類繼承自Person父類,同時又具備摻元類Mixin的方法和屬性,如果你願意,可以定義N個摻元類用來擴充該類, 它同樣能夠繼承你定義的其他摻元類的屬性和方法,這樣就實現了多重繼承,最後,author的serialize方法的運行結果如下:

你會發現該類同時具有person類,Author類,Mixin類的屬性和方法,其中Person和Mixin的屬性和方法都是通過“繼承”得來的,從實際上來講,它實現了多重繼承。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章