構造函數繼承(類繼承)編程
原型繼承設計模式
摻元類(拷貝繼承)數組
構造函數繼承的實現方式主要是藉助於JavaScript中的 call , apply 方法能夠綁定this的特色,以及使用 new 關鍵字生成實例,模仿傳統的繼承方式,然後者的繼承纔是JavaScript語言自己提倡的繼承方式。相比於類繼承,原型繼承具備節約內存,效率較高等特色,代碼也更優雅。下面以具體的例子來簡單說明下三種繼承的不一樣。app
好比目前有兩個類,分別是函數
function super(id){學習
this.superId = id;this
}prototype
和設計
function sub(name){對象
this.name = name;
}
要讓sub繼承super,使用類繼承能夠這樣實現
function sub(id,name){
super.call(this,id);
this.name = name;
}
這樣的方式也能夠實現多重繼承,只要多加幾條 call 或者 appley 語句便可。
使用這種方法的缺點是每次實例化一個對象都會爲對象中的屬性和方法建立新的副本,浪費內存,並且這樣的繼承沒法用 instanceof 操做符來判斷。
而原型繼承則是
sub.prototype = new super();
sub.prototype.constructor = sub;
這是最簡單最少語句的原型繼承實現。已經能夠實現咱們要的效果,不過還能夠繼續改進。
在sub對象中,不變的屬性和方法咱們均可以放在其原型中好比:
function super(){}
super.prototype.superId = 100086;
而後咱們可讓sub直接繼承super的原型
sub.prototype = super.prototype;
sub.prototype.constructor = sub;
這樣寫的好處在於能夠避免建立超類的新實例,由於它可能很是大或超類的構造函數有一些其餘的反作用好比要進行一些複雜的運算,總之,能夠減小這方面時間的花費,減小內存的佔用,提升代碼效率,固然也還有一些缺點,那就是如今的 sub.prototype 與 super.prototype 指向了同一個對象,那麼對sub.prototype的任何修改都會反映到super.prototype上,若是此時還有其餘的對象繼承了super.prototype那麼,就會直接影響到其餘的對象,這不是咱們所但願看到的。並且再看這句代碼:
sub.prototype.constructor = sub;
這句代碼的本意是想修復sub.prototype的constructor屬性,而如今將會連同super.prototype.constructor也一塊兒修改了。
因此以上的原型繼承還須要繼續完善,看下面:
var F = function(){};
F.prototype = super.prototype;
sub.prototype = new F();
sub.prototype.constructor = sub;
這裏藉助了一個空對象做爲中介,這樣上面所提到的問題就解決了,改變sub.prototype只會修改F的實例,而不會影響到super.prototypel了,並且因爲F是空對象,幾乎不佔內存。將上面的方法封裝成一個函數
function extend(sub, super) {
var F = function(){};
F.prototype = super.prototype;
sub.prototype = new F();
sub.prototype.constructor = sub;
sub.super= super.prototype;
if(super.prototype.constructor == Object.prototype.constructor){
super.prototype.constructor = super
}
}
上面的最終版本多出了額外的幾行代碼,它的做用是提供給sub一個super屬性,來弱化子類與超類之間的耦合關係,而且確保超類的的prototype的constructor屬性已經被正確設置,即便超類就是Object自己。這在既要調用超類的方法又想重寫超類的某個方法時能夠派上用場,能夠直接經過super屬性來訪問。聽說這個函數就是YUI庫的繼承方式。
除了上面兩種繼承方式外,還有一種方法——使用摻元類,經過對象擴充的方式讓這些類包含的方法共享給其餘類,直觀點就是拷貝複製。
function augment(reveivingClass, givingClass){
for(methodName in givingClass.prototype){
if(!receivingClass.prototype[methodName]){
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}
上面就是一個簡單的擴充方法。還能夠改進一下,若是摻元類中包含許多方法,但咱們只想要複製其中的一兩個,那麼上面這個函數還能夠再添加一個參數表示要拷貝的方法數組
function augment(reveivingClass, givingClass, copyArr){
if(copyArr){
for(var i = 0; i < copyArr.length; i++){
var methodName = copyArr[i];
if(!receivingClass.prototype[methodName]){
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}else{
for(var methodName in givingClass.prototype){
if(!receivingClass.prototype[methodName]){
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}
}
如今就可使用 augment(sub, super, [‘trim’]) 這樣的語句來達到只爲sub類添加一個trim方法的目的了,若是想添加更多的方法,只要把方法方法名傳入數組便可。
用這樣的方法擴充一個類有時比繼承另外一個類更合適。
此次主要學習了繼承的幾種不一樣實現方式,以及每種方法的優缺點,主要參考綜合了阮一峯大神的關於JavaScript面向對象編程系列的博文以及JavaScript設計模式繼承章節的內容。