讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議94:建議使用封裝類繼承 >

建議94:建議使用封裝類繼承

在面向對像編程中,語言自身都有一套嚴格的封裝機制,開發人員只是按慣性思維去開發具體的項目,很少關心封裝問題,因為語言會自動完成基本的功能封裝,當然具體應用的功能還需要程序人員自己去封裝。但是,JavaScript語言沒有提供良好的封裝機制,只能夠依靠開發人員的方法來實現部分功能封裝。類繼承是在JavaScript程序開發中應用得比較廣泛的繼承模式,為了更方便地使用,建議讀者對這種模式進行規範和封裝,以便提高代碼利用率。

首先,定義一個封裝函數。設計入口為子類和超類對象,函數功能是子類能夠繼承超類的所有原型成員,不設計出口:


function extend(Sub,Sup){//類繼承封裝函數

//參數Sub表示子類,Sup表示超類

}


在函數體內,首先定義一個空函數F,用來實現功能中轉。設計F的原型為超類的原型,然後把空函數的實例傳遞給子類的原型,這樣就避免了直接實例化超類可能帶來的系統負荷。在實際開發中,超類的規模可能會很大,進行實例化會佔用大量內存。

恢復子類原型的構造器子類,同時,檢測超類的原型構造器是否與Object的原型構造器發生耦合,如果是,則恢復它的構造器為超類自身。


function extend(Sub,Sup){//類繼承封裝函數

var F=function{};//定義一個空函數

F.prototype=Sup.prototype;//設置空函數的原型為超類的原型

Sub.prototype=new F;//實例化空函數,並把超類原型引用傳遞給子類

Sub.prototype.constructor=Sub;//恢復子類原型的構造器為子類自身

Sub.sup=Sup.prototype;//在子類中存儲超類原型,避免子類和超類耦合

if(Sup.prototype.constructor==Object.prototype.constructor){//檢測超類原型構造器是否為自身

Sup.prototype.constructor=Sup//類繼承封裝函數

}

}


一個簡單的功能封裝函數就這樣實現了。下面定義兩個類,嘗試把它們綁定為繼承關係。


function A(x){//構造函數A

this.x=x;

this.get=function{

return this.x;

}

}

A.prototype.add=function{

return this.x+this.x;

}

A.prototype.mul=function{

return this.x*this.x;

}

function B(x){//構造函數B

A.call(this,x);//在函數體內調用構造函數A,實現內部數據綁定

}

extend(B,A);//調用類繼承封裝函數,把A和B的原型捆綁在一起

var f=new B(5);

alert(f.get)//5

alert(f.add)//10

alert(f.mul)//25


在類繼承封裝函數中,有這樣的語句「Sub.sup=Sup.prototype;」,在上面的代碼中沒有體現,為了理解它的價值,先看下面的代碼:


extend(B,A);

B.prototype.add=function{//為B類定義一個原型方法

return this.x+""+this.x

}


上面的代碼是在調用封裝函數之後再為B類定義了一個原型方法,該方法名與A類的原型方法add同名,但功能不同。如果此時測試程序,那麼會發現子類B定義的原型方法add將會覆蓋超類A的原型方法add,如下:


alert(f.add)//字符串55,而不是數值10


在B類的原型方法add中調用超類的原型方法add,從而避免代碼耦合的現象發生:


B.prototype.add=function{

return B.sup.add.call(this);//在函數內部調用超類的方法add

}