02.Javascript中的繼承----Inherits
本文不再過多的闡述OOP中繼承的概念,只是用原生的Javascript代碼來類比類繼承(不是對象擴充)
類繼承:inherits
假設有已定義好的超類(父類)SuperClass和待繼承的子類SubClass,於是,可以定義如下的方法來實作類別繼承
inherits方法的定義
如下的這個inherits方法,其實現思想主要參考《Pro JavaScript Design Patterns》【Ross Harmes and Dustin Diaz】
/** * 這個方法用來實作類別繼承 * @param {function} subClass 待繼承的子類 * @param {function} superClass 待被繼承的父類 * @exception {Error} 參數不合法時拋出異常 */var inherits = function(subClass,superClass){ if(arguments.length !== 2){ throw new Error("必須明確的指定子類和父類"); } for(var i = 0,n = arguments.length;i < n;i++){ if(typeof arguments[i] !== "function"){ throw new Errorr("所給的子類和父類必須都是function"); } } var F = function(){}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; };
該方法的內部會進行嚴格的參數合法性檢查,參數必須是兩個,即一個子類,一個父類;另外還要求兩個參數都必須是function類型,否則拋出異常,宣告繼承失敗
inherits方法的使用▲第一步
/** * 定義父類 */var SuperClass = function(){};SuperClass.prototype = { name : "趙先烈", talk : function(){ alert("我的名字叫" + this.name); }}; /** * 這裡定義子類 */var SubClass = function(){}; //實施繼承inherits(SubClass,SuperClass);//建立子類並從調用從子類繼承過來的方法var oSub = new SubClass();oSub.talk(); //這裡輸出:我的名字叫趙先烈
從程式的執行結果可以看出,這種繼承方法是將父類中的全部內容(除構造器以外)拷貝到子類中來,順利的完成了類繼承。
▲第二步
/** * 這裡定義父類 */var SuperClass = function(){ this.name = "趙先烈"; this.talk = function(){ alert("我的名字叫" + this.name); };};/** * 這裡定義子類 */var SubClass = function(){ //這是一個空類};//實施繼承inherits(SubClass,SuperClass);//建立子類對象,並調用從父類繼承過來的方法var oSub = new SubClass();oSub.talk(); //按照繼承的概念,這裡應該輸出:我的名字叫趙先烈
程式真正執行後,才發現發生異常了,原因:在oSub執行個體中不存在talk方法。不是已經通過inherits方法實現繼承了麼,為什麼會沒有這個
方法呢?探究其原因,是因為在inherits的內部,只是將SuperClass的原型鏈原樣的拷貝給了SubClass,而不在SuperClass
原型鏈中的屬性及方法,子類就拿不到了。
那麼,既要支援第一種繼承,也要能滿足第二種,我們只能再次對inherits方法進行升級了,將其改為如下形式:
/** * 這個方法用來實作類別繼承 * @param {function} subClass 待繼承的子類 * @param {function} superClass 待被繼承的父類 * @exception {Error} 參數不合法時拋出異常 */var inherits = function(subClass,superClass){ if(arguments.length !== 2){ throw new Error("必須明確的指定子類和父類"); } for(var i = 0,n = arguments.length;i < n;i++){ if(typeof arguments[i] !== "function"){ throw new Errorr("所給的子類和父類必須都是function"); } } subClass.prototype = new superClass(); subClass.prototype.constructor = subClass;};
經過這樣的改動,第一步和第二步中的情況就都能順利的正常的執行了。
▲第三步
如果在第二步的基礎上,將代碼中的SubClass.prototype改為如下形式:
SubClass.prototype = { walk : function(){ alert("偶也,我可以走路了哎"); }, talk : function(){ alert("我的名字不叫" + this.name); }
};
再執行如下代碼:
inherits(SubClass,SuperClass);var oSub = new SubClass();oSub.talk(); //這裡仍然輸出:我的名字叫趙先烈//重點在下面這句oSub.walk(); //這裡會拋出異常,告訴我們,walk方法不存在
分析這個結果,oSub.talk()的輸出應該為:我的名字不叫趙先烈。並且oSub.walk()也應該有輸出,程式應該正常執行才對。但是,為什麼會有這樣的運行結果呢?
這到底是為什麼呢?相信你一定早就發現了,在inherits方法中有這樣一句關鍵的代碼:
subClass.prototype = new superClass();
這裡已經告訴我們,原來,子類中的原型鏈已經被完全替換了,其實仔細說起來,這樣做是有違繼承的原則的(一般不都是子類覆蓋父類嗎?怎麼
反過來了呢)。
但是這樣的問題我們始終要解決,必須讓這個所謂的繼承能同時滿足以上三種情況都能正常執行。苦思冥想後,決定改成這樣:
/** * 這個方法用來實作類別繼承 * @param {function} subClass 待繼承的子類 * @param {function} superClass 待被繼承的父類 * @exception {Error} 參數不合法時拋出異常 */var inherits = function(subClass,superClass){ if(arguments.length !== 2){ throw new Error("必須明確的指定子類和父類"); } for(var i = 0,n = arguments.length;i < n;i++){ if(typeof arguments[i] !== "function"){ throw new Errorr("所給的子類和父類必須都是function"); } } var oSuper = new superClass(); for(var key in oSuper){ if(!subClass.prototype[key]){ subClass.prototype[key] = oSuper[key]; } } subClass.prototype.constructor = subClass;};
OK,通過這樣的升級,再次運行上面的代碼,順利通過了。
上面這個通過兩次升級的inherits方法也是目前比較穩定的版本了,在後續的文章中所指的inherits方法,就是這個了。
本文旨在共同探討Javascript中類的繼承方式,只是其中的一種類比方式,類比之不當,還請見諒。
歡迎提出寶貴的升級建議。