JavaScript類的繼承

來源:互聯網
上載者:User
利用共用prototype實現繼承
繼承是物件導向開發的又一個重要概念,它可以將現實生活的概念對應到程式邏輯中。例如水果是一個類,具有一些公用的性質;而蘋果也是一類,但它們屬於水果,所以蘋果應該繼承於水果。
在JavaScript中沒有專門的機制來實作類別的繼承,但可以通過拷貝一個類的prototype到另外一個類來實現繼承。一種簡單的實現如下:
fucntion class1(){
      //建構函式
}

function class2(){
      //建構函式
}
class2.prototype=class1.prototype;
class2.prototype.moreProperty1="xxx";
class2.prototype.moreMethod1=function(){
      //方法實現代碼
}
var obj=new class2();
這樣,首先是class2具有了和class1一樣的prototype,不考慮建構函式,兩個類是等價的。隨後,又通過prototype給class2賦予了兩個額外的方法。所以class2是在class1的基礎上增加了屬性和方法,這就實現了類的繼承。
JavaScript提供了instanceof操作符來判斷一個對象是否是某個類的執行個體,對於上面建立的obj對象,下面兩條語句都是成立的:
obj instanceof class1
obj instanceof class2
表面上看,上面的實現完全可行,JavaScript也能夠正確的理解這種繼承關係,obj同時是class1和class2的執行個體。事是上不 對,JavaScript的這種理解實際上是基於一種很簡單的策略。看下面的代碼,先使用prototype讓class2繼承於class1,再在 class2中重複定義method方法:
<script language="JavaScript" type="text/javascript">
<!--
//定義class1
function class1(){
      //建構函式
}
//定義class1的成員
class1.prototype={
      m1:function(){
            alert(1);
      }
}
//定義class2
function class2(){
      //建構函式
}
//讓class2繼承於class1
class2.prototype=class1.prototype;
//給class2重複定義方法method
class2.prototype.method=function(){
      alert(2);
}
//建立兩個類的執行個體
var obj1=new class1();
var obj2=new class2();
//分別調用兩個對象的method方法
obj1.method();
obj2.method();
//-->
</script>
從代碼執行結果看,彈出了兩次對話方塊“2”。由此可見,當對class2進行prototype的改變時,class1的prototype也隨之改變, 即使對class2的prototype增減一些成員,class1的成員也隨之改變。所以class1和class2僅僅是建構函式不同的兩個類,它們 保持著相同的成員定義。從這裡,相信讀者已經發現了其中的奧妙:class1和class2的prototype是完全相同的,是對同一個對象的引用。其 實從這條指派陳述式就可以看出來:
//讓class2繼承於class1
class2.prototype=class1.prototype;
在JavaScript中,除了基本的資料類型(數字、字串、布爾等),所有的賦值以及函數參數都是引用傳遞,而不是值傳遞。所以上面的語句僅僅是讓 class2的prototype對象引用class1的prototype,造成了類成員定義始終保持一致的效果。從這裡也看到了instanceof 操作符的執行機制,它就是判斷一個對象是否是一個prototype的執行個體,因為這裡的obj1和obj2都是對應於同一個prototype,所以它們 instanceof的結果都是相同的。
因此,使用prototype引用拷貝實現繼承不是一種正確的辦法。但在要求不嚴格的情況下,卻也是一種合理的方法,惟一的約束是不允許類成員的覆蓋定義。下面一節,將利用反射機制和prototype來實現正確的類繼承。
利用反射機制和prototype實現繼承
前面一節介紹的共用prototype來實作類別的繼承,不是一種很好的方法,畢竟兩個類是共用的一個 prototype,任何對成員的重定義都會互相影響,不是嚴格意義的繼承。但在這個思想的基礎上,可以利用反射機制來實作類別的繼承,思路如下:利用 for(…in…)語句枚舉出所有基類prototype的成員,並將其賦值給子類的prototype對象。例如:
<script language="JavaScript" type="text/javascript">
<!--
function class1(){
      //建構函式
}
class1.prototype={
      method:function(){
           alert(1);
      },
      method2:function(){
           alert("method2");
      }
}
function class2(){
      //建構函式
}
//讓class2繼承於class1
for(var p in class1.prototype){
       class2.prototype[p]=class1.prototype[p];
}

//覆蓋定義class1中的method方法
class2.prototype.method=function(){
      alert(2);
}
//建立兩個類的執行個體
var obj1=new class1();
var obj2=new class2();
//分別調用obj1和obj2的method方法
obj1.method();
obj2.method();
//分別調用obj1和obj2的method2方法
obj1.method2();
obj2.method2();
//-->
</script>
從運行結果可見,obj2中重複定義的method已經覆蓋了繼承的method方法,同時method2方法未受影響。而且obj1中的method方 法仍然保持了原有的定義。這樣,就實現了正確意義的類的繼承。為了方便開發,可以為每個類添加一個共有的方法,用以實作類別的繼承:
//為類添加靜態方法inherit表示繼承於某類
Function.prototype.inherit=function(baseClass){
     for(var p in baseClass.prototype){
            this.prototype[p]=baseClass.prototype[p];
     }
}
這裡使用所有函數對象(類)的共同類Function來添加繼承方法,這樣所有的類都會有一個inherit方法,用以實現繼承,讀者可以仔細理解這種用法。於是,上面代碼中的:
//讓class2繼承於class1
for(var p in class1.prototype){
       class2.prototype[p]=class1.prototype[p];
}
可以改寫為:
//讓class2繼承於class1
class2.inherit(class1)
這樣代碼邏輯變的更加清楚,也更容易理解。通過這種方法實現的繼承,有一個缺點,就是在class2中添加類成員定義時,不能給prototype直接賦值,而只能對其屬性進行賦值,例如不能寫為:
class2.prototype={
      //成員定義
}
而只能寫為:
class2.prototype.propertyName=someValue;
class2.prototype.methodName=function(){
      //語句
}
由此可見,這樣實現繼承仍然要以犧牲一定的代碼可讀性為代價,在下一節將介紹prototype-1.3.1架構(註:prototype-1.3.1框 架是一個JavaScript類庫,擴充了基本對象功能,並提供了工具 + 生產力詳見附錄。)中實現的類的繼承機制,不僅基類可以用對象直接賦值給 property,而且在衍生類別中也可以同樣實現,使代碼邏輯更加清晰,也更能體現物件導向的語言特點。

prototype-1.3.1架構中的類繼承實現機制
在prototype-1.3.1架構中,首先為每個對象都定義了一個extend方法:
//為Object類添加靜態方法:extend
Object.extend = function(destination, source) {
  for(property in source) {
     destination[property] = source[property];
  }
  return destination;
}
//通過Object類為每個對象添加方法extend
Object.prototype.extend = function(object) {
  return Object.extend.apply(this, [this, object]);
}
Object.extend方法很容易理解,它是Object類的一個靜態方法,用於將參數中source的所有屬性都賦值到destination對象 中,並返回destination的引用。下面解釋一下Object.prototype.extend的實現,因為Object是所有對象的基類,所以 這裡是為所有的對象都添加一個extend方法,函數體中的語句如下:
Object.extend.apply(this,[this,object]);
這一句是將Object類的靜態方法作為對象的方法運行,第一個參數this是指向對象執行個體自身;第二個參數是一個數組,包括兩個元素:對象本身和傳進來 的對象參數object。函數功能是將參數對象object的所有屬性和方法賦值給調用該方法的對象自身,並返回自身的引用。有了這個方法,下面看類繼承 的實現:
<script language="JavaScript" type="text/javascript">
<!--
//定義extend方法
Object.extend = function(destination, source) {
  for (property in source) {
     destination[property] = source[property];
  }
  return destination;
}
Object.prototype.extend = function(object) {
  return Object.extend.apply(this, [this, object]);
}
//定義class1
function class1(){
      //建構函式
}
//定義類class1的成員
class1.prototype={
      method:function(){
           alert("class1");
      },
      method2:function(){
           alert("method2");
      }

}
//定義class2
function class2(){
      //建構函式
}
//讓class2繼承於class1並定義新成員
class2.prototype=(new class1()).extend({
      method:function(){
           alert("class2");
      }
});

//建立兩個執行個體
var obj1=new class1();
var obj2=new class2();
//實驗obj1和obj2的方法
obj1.method();
obj2.method();
obj1.method2();
obj2.method2();
//-->
</script>
從運行結果可以看出,繼承被正確的實現了,而且衍生類別的額外成員也可以以列表的形式加以定義,提高了代碼的可讀性。下面解釋繼承的實現:
//讓class2繼承於class1並定義新成員
class2.prototype=(new class1()).extend({
      method:function(){
           alert("class2");
      }
});
上段代碼也可以寫為:
//讓class2繼承於class1並定義新成員
class2.prototype=class1.prototype.extend({
      method:function(){
           alert("class2");
      }
});
但因為extend方法會改變調用該方法對象本身,所以上述調用會改變class1的prototype的值,犯了和以前一樣的錯誤。在 prototype-1.3.1架構中,巧妙的利用new class1()來建立一個執行個體對象,並將執行個體對象的成員賦值給class2的prototype。其本質相當於建立了class1的prototype 的一個拷貝,在這個拷貝上進行操作自然不會影響原有類中prototype的定義了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.