javascript:物件導向編程基礎:繼承

來源:互聯網
上載者:User
"繼承是物件導向開發的又一個重要概念,它可以將現實生活的概念對應帶程式邏輯中"。“  雖然在JavaScript中沒有專門的機制來實作類別的繼承,但可以通過拷貝一個類的prototype 到另外一個類來實現繼承”。我們看到這裡繼承的概念是多麼的直白,“拷貝一個類的prototype 到另外一個類”,好,Code is cheap,看代碼:Code
function class1() { }
function class2() { }
class2.prototype = class1.prototype;
class2.moreProperty1 = "class 2 additional string";
class2.moreMethod1 = function() { alert("class 2 additional method"); }
/*
 這樣,首先是class2具有了和class1 一樣的prototype,不考慮建構函式,兩個類是等價的。
 隨後,又通過prototype給class2賦予了兩個額外的方法。所以class2是在class1的基礎上
 增加了屬性和方法,這就實現了類的繼承。
*/

function test() {
    var obj = new class2();
    // JavaScript提供了instanceof 操作符來判斷一個對象是否是某個類的執行個體
    alert(obj instanceof class2); // true
    alert(obj instanceof class1); // ?
}

運行代碼,結果是不是在我們的意料之中?表面上看,上面的實現完全可行,js也可以正確的理解和實現這種繼承關係,obj同時是class1和class2的執行個體,但實質上則不然(我們學習的目的是要知其然更要知其所以然)。js的這種理解實際上是基於一種很簡單的策略,看下面的代碼,先使用prototype讓class2 繼承於class1,再在class2 中重複定義method 方法:Code
//定義class1 
function class1() {
    //建構函式 
}
//定義class1 的成員 
class1.prototype = {
    m1: function() { // 方法1
        alert("class1 method1");
    }
}
//定義class2 
function class2() {
    //建構函式 
}
//讓class2 繼承於class1
class2.prototype = class1.prototype;

//給class2 重複定義方法method 
class2.prototype.method = function() {
    alert("whose method2? class1 or class2");
}
//建立兩個類的執行個體
var obj1 = new class1();
var obj2 = new class2();

function test() {
    //分別調用兩個對象的method 方法 
    obj1.method();
    obj2.method();
}

從代碼執行結果看,method方法在class1,2中啟動並執行結果是相同的。

由此可見,當對class2 進行prototype  的改變時,class1的prototype也隨之改變,即使對class2  的prototype  增減一些成員,class1的成員也隨之改變。所以class1 和class2 僅僅是建構函式不同的兩個類,它們保持著相同的成員定義。說到這裡,相信讀者已經發現了其中的奧妙:class1 和class2 的prototype 是完全相同的,是對同一個對象的引用。其實從這條指派陳述式就可以看出來: 
 //讓class2 繼承於class1 
 class2.prototype=class1.prototype;
   在js中,除了基本的資料類型(數字、字串、布爾類型等),所有的賦值以及函 數參數都是引用傳遞,而不是值傳遞。所以上面的語句僅僅是讓class2 的prototype 對象引用class1 的prototype,造成了類成員定義始終保持一致的效果。從這裡也看到了instanceof 操作符的執行機制,它就是判斷一個對象是否是一個prototype 的執行個體,因為這裡的obj1 和obj2  都是對應於同一個prototype,所以它們instanceof 的結果都是相同的。由此可見,使用prototype  引用拷貝實現繼承不是一種正確的辦法。但在要求不嚴格的情況下,卻也是一種合理的方法,唯一的約束是不允許類成員的覆蓋定義(這裡其實也是js的靈活性的體現)。其實,我們完全可以利用反射機制和prototype 來實現js正確的類繼承:

Code
function class1() {
    //建構函式 
}
class1.prototype = {
    method: function() {
        alert("method1");
    },
    method2: function() {
        alert("method2");
    }
}
function class2() {
    //建構函式
}

//讓class2 繼承於class1 
for (var p in class1.prototype) {
    class2.prototype[p] = class1.prototype[p]; // 利用反射機制和prototype實現繼承
}

//覆蓋定義class1中的method 方法 
class2.prototype.method = function() {
    alert("class2 new method1");
}

//建立兩個類的執行個體
var obj1 = new class1();
var obj2 = new class2();

function test() {
    //分別調用兩個對象的method 方法 
    obj1.method();
    obj2.method();
    //分別調用兩個對象的method2 方法
    obj1.method2();
    obj2.method2();
}

從運行結果可見,obj2中重複定義的method 已經覆蓋了繼承的method方法,同時method2 方法未受影響。而且obj1中的method 方法仍然保持了原有的定義。這樣,就實現了正確意義的類的繼承。為了方便開發,可以為每個類添加一個共有的方法,用以實作類別的繼承: Code
//為類添加靜態方法inherit表示繼承於某類 
Function.prototype.inherit = function(baseClass) {
    for (var p in baseClass.prototype) {
        this.prototype[p] = baseClass.prototype[p];
    }
}

function class1() {
    //建構函式 
}
class1.prototype = {
    method: function() {
        alert("method1");
    },
    method2: function() {
        alert("method2");
    }
}
function class2() {
    //建構函式
}

//讓class2 繼承於class1 
//for (var p in class1.prototype) {
//    class2.prototype[p] = class1.prototype[p]; // 利用反射機制和prototype實現繼承
//}

class2.inherit(class1); // 等價於上面注釋掉的那一個for迴圈

//覆蓋定義class1中的method 方法 
class2.prototype.method = function() {
    alert("class2 new method1");
}

//建立兩個類的執行個體
var obj1 = new class1();
var obj2 = new class2();

function test() {
    //分別調用兩個對象的method 方法 
    obj1.method();
    obj2.method();
    //分別調用兩個對象的method2 方法
    obj1.method2();
    obj2.method2();
}

上面的代碼使邏輯變的更加清楚,也更容易理解。通過這種方法實現的繼承,有一個缺點,就是在class2 中添加類成員定義時,不能給prototype 直接賦值,而只能對其屬性進行賦值,例如不能為:
class2.prototype={
                           //成員定義
                      } 
 而只能為:               
    class2.prototype.propertyName=someValue; 
    class2.prototype.methodName=function(){ 
                         //語句
                      }

   由此可見,這樣實現繼承仍然要以犧牲一定的代碼可讀性為代價。有沒有“不僅基類可以用對象直接賦值給property,而且在衍生類別中也可以同樣實現,使代碼邏輯更加清晰,也更能體現物件導向的語言特點”的js繼承方式?引號裡的說法是多麼的誘人啊,繼續學習去了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.