在面向對像編程中,語言自身都有一套嚴格的封裝機制,開發人員只是按慣性思維去開發具體的項目,很少關心封裝問題,因為語言會自動完成基本的功能封裝,當然具體應用的功能還需要程序人員自己去封裝。但是,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
}