JavaScript中實(shí)現(xiàn)繼承的三種方式和實(shí)例
javascript雖然是一門面向?qū)ο蟮恼Z言,但是它的繼承機(jī)制從一開始設(shè)計(jì)的時(shí)候就不同于傳統(tǒng)的其他面向?qū)ο笳Z言,是基于原型的繼承機(jī)制,但是在這種機(jī)制下,繼承依然有一些不同的實(shí)現(xiàn)方式。
方法一:類式繼承
所謂的類式繼承就是指模仿傳統(tǒng)面向?qū)ο笳Z言的繼承方式,繼承與被繼承的雙方都是“類”,代碼如下:
首先定義一個(gè)父類(或超類):
function Person(name){
this.name=name;
}
Person.prototype.getName=function(){
return this.name;
};
該父類person的屬性在構(gòu)造函數(shù)中定義,可以保證繼承它的子類的name屬性不與它共享這個(gè)屬性,而是單獨(dú)屬于子類,將getName方法掛載到原型上,是為了讓繼承它的子類的多個(gè)實(shí)例共享這一個(gè)方法體,這樣能夠節(jié)省內(nèi)存,(對于多個(gè)實(shí)例來講,每次New一個(gè)實(shí)例出來都會(huì)保證這些實(shí)例的getName方法引用的是同一段內(nèi)存空間,而不是獨(dú)立的空間)。
定義一個(gè)繼承方法extend,如下:
function extend(subClass,superClass){
var F=function(){};
F.prototype=superClass.prototype;
subClass.prototype=new F();
subClass.prototype.constructor=subClass;
subClass.superClass=superClass.prototype;
if(superClass.prototype.constructor==Object.prototype.constructor){
superClass.prototype.constructor=superClass;
}
}
在這個(gè)方法中,首先創(chuàng)建一個(gè)新的類為F,讓它的原型為父類的原型,并且讓子類的原型指向該類F的一個(gè)實(shí)例,從而達(dá)到了一個(gè)繼承父類的目的,同時(shí),子類的原型由于被修改,所以將修改后的原型的constructor屬性指向子類,讓其擁有構(gòu)造函數(shù),同時(shí)給該子類掛載一個(gè)superClass屬性,子類可以通過該屬性調(diào)用父類,從而建立起了子類和父類的關(guān)系。
定義一個(gè)子類Author來繼承父類Person,如下:
function Author(name,books){
Author.superClass.constructor.call(this,name);
this.book=books;
}
extend(Author,Person);
Author.prototype.getBooks=function(){
return this.book;
}
這里在子類的構(gòu)造函數(shù)中通過其superClass屬性調(diào)用父類的構(gòu)造函數(shù),同時(shí)采用call方法,轉(zhuǎn)換該方法調(diào)用的this指向,讓子類Author也擁有(繼承)父類的屬性,同時(shí)子類又擁有自己的屬性book,所以在構(gòu)造函數(shù)中將參數(shù)books賦值給屬性book,達(dá)到構(gòu)造的目的。采用extend函數(shù)繼承父類Person原型上的屬性和方法(實(shí)際上只繼承了方法,因?yàn)槲覀冎爸皇菍⒎椒⊕燧d到了原型上,屬性是在構(gòu)造函數(shù)中定義的)。同時(shí),Author又擁有自己的方法getBooks,將其掛載到對應(yīng)的原型上,達(dá)到了在繼承的基礎(chǔ)上進(jìn)一步擴(kuò)充自身的目的。
這種繼承方式很明顯就是采用類似于傳統(tǒng)面向?qū)ο笳Z言的類式繼承,優(yōu)點(diǎn)是對習(xí)慣于傳統(tǒng)面向?qū)ο蟾拍畹某绦騿T來講很容易理解,缺點(diǎn)是過程比較繁瑣,內(nèi)存消耗稍大,因?yàn)樽宇愐矒碛凶约旱臉?gòu)造函數(shù)及原型,而且子類和父類的屬性完全是隔離的,即使兩者是一樣的值,但是不能共享同一段內(nèi)存。
方法二:原型式繼承
首先定義一個(gè)父類,這里不會(huì)刻意模仿使用構(gòu)造函數(shù)來定義,而是直接采用對象字面量的方式定義一個(gè)對象,該對象就是父類
var Person={
name:'default name',
getName:function(){
return this.name;
}
} ;
和第一種方法一樣,該對象擁有一個(gè)屬性name和一個(gè)方法getName .
然后再定義一個(gè)克隆方法,用來實(shí)現(xiàn)子類對父類的繼承,如下:
function clone(obj){
function F(){}
F.prototype=obj;
return new F();
}
該克隆方法新建一個(gè)對象,將該對象的原型指向父類即參數(shù)obj,同時(shí)返回這個(gè)對象。
最后子類再通過克隆函數(shù)繼承父類,如下:
var Author=clone(Person);
Author.book=['javascript'];
Author.showBook=function(){
return this.book;
}
這里定義一個(gè)子類,通過clone函數(shù)繼承父類Person,同時(shí)拓展了一個(gè)屬性book,和一個(gè)方法showBook,這里該子類也擁有屬性name,但是它和父類的name值是一樣的,所以沒有進(jìn)行覆蓋,如果不一樣,可以采用
Author.name='new name';覆蓋這個(gè)屬性,從而得到子類的一個(gè)新的name屬性值。
這種原型式繼承相比于類式繼承更為簡單自然,同時(shí)如果子類的屬性和父類屬性值相同,可以不進(jìn)行修改的話,那么它們兩者其實(shí)共享的是同一段內(nèi)存空間,如上面的name屬性,缺點(diǎn)是對于習(xí)慣了傳統(tǒng)面向?qū)ο蟮某绦騿T難以理解,如果兩者要進(jìn)行選擇的話,無疑是這種方式更為優(yōu)秀一些。
既然javascript中采用基于原型的方式來實(shí)現(xiàn)繼承,而且每個(gè)對象的原型只能指向某個(gè)特定的類的實(shí)例(不能指向多個(gè)實(shí)例),那么如何實(shí)現(xiàn)多重繼承(即讓一個(gè)類同時(shí)具有多個(gè)類的方法和屬性,而且本身內(nèi)部不自己定義這些方法和屬性)?
在javascript設(shè)計(jì)模式中給出了一種摻元類(mixin class)的方式:
首先定義一個(gè)摻元類,用來保存一些常用的方法和屬性,這些方法和屬性可以通過拓展的方式添加到任何其他類上,從而被添加類就具有了該類的某些方法和屬性,如果定義多個(gè)摻元類,同時(shí)添加給一個(gè)類,那么該類就是間接實(shí)現(xiàn)了“多重繼承”,基于這種思想,實(shí)現(xiàn)如下:
摻元類定義:
var Mixin=function(){};
Mixin.prototype={
serialize:function(){
var output=[];
for(key in this){
output.push(key+":"+this[key]);
}
return output.join(',');
}
}
該摻元類具有一個(gè)serialize方法,用來遍歷其自身,輸出自身的屬性和屬性值,并且將他們以字符串形式返回,中間用逗號(hào)隔開。
定義一個(gè)擴(kuò)充方法,用來使某個(gè)類經(jīng)過擴(kuò)充之后具備摻元類的屬性或方法,如下:
function augment(receivingClass,givingClass){
if(arguments[2]){
for(var i= 2,len=arguments.length;i<len;i++){
receivingClass.prototype[arguments[i]]=givingClass.prototype[arguments[i]];
}
}
else
{
for(methodName in givingClass.prototype){
if(!receivingClass.prototype[methodName]){
receivingClass.prototype[methodName]=givingClass.prototype[methodName];
}
}
}
}
該方法默認(rèn)是兩個(gè)參數(shù),第一個(gè)參數(shù)是接受被擴(kuò)充的類,第二個(gè)參數(shù)是摻元類(用來擴(kuò)充其他類的類),還可以有其他參數(shù),如果大于兩個(gè)參數(shù),后面的參數(shù)都是方法或者屬性名,用來表示被擴(kuò)充類只想繼承摻元類的指定的屬性或方法,否則默認(rèn)繼承摻元類的所有屬性和方法,在該函數(shù)中,第一個(gè)if分支是用來繼承指定屬性和方法的情形,else分支是默認(rèn)繼承所有屬性和方法的情形。該方法的實(shí)質(zhì)是將摻元類的原型上的屬性和方法都擴(kuò)充(添加)到了被擴(kuò)充類的原型上面,從而使其具有摻元類的屬性和方法。
最后,使用擴(kuò)充方法實(shí)現(xiàn)多重繼承
augment(Author,Mixin);
var author= new Author('js',['javascript design patterns']);
alert(author.serialize());
這里定義了一個(gè)author的類,該類繼承自Person父類,同時(shí)又具備摻元類Mixin的方法和屬性,如果你愿意,可以定義N個(gè)摻元類用來擴(kuò)充該類,它同樣能夠繼承你定義的其他摻元類的屬性和方法,這樣就實(shí)現(xiàn)了多重繼承,最后,author的serialize方法的運(yùn)行結(jié)果如下:

你會(huì)發(fā)現(xiàn)該類同時(shí)具有person類,Author類,Mixin類的屬性和方法,其中Person和Mixin的屬性和方法都是通過“繼承”得來的,從實(shí)際上來講,它實(shí)現(xiàn)了多重繼承。
相關(guān)文章
JavaScript 動(dòng)態(tài)將數(shù)字金額轉(zhuǎn)化為中文大寫金額
JavaScript 將數(shù)字金額轉(zhuǎn)化為中文大寫金額的函數(shù)2009-05-05
JS實(shí)現(xiàn)彈出浮動(dòng)窗口(支持鼠標(biāo)拖動(dòng)和關(guān)閉)實(shí)例詳解
這篇文章主要介紹了JS實(shí)現(xiàn)彈出浮動(dòng)窗口,可支持鼠標(biāo)拖動(dòng)和關(guān)閉的功能,界面美觀大方,涉及javascript動(dòng)態(tài)創(chuàng)建對話框的相關(guān)技巧,需要的朋友可以參考下2015-08-08
微信小程序云開發(fā)實(shí)現(xiàn)增刪改查功能
這篇文章主要為大家詳細(xì)介紹了微信小程序云開發(fā)實(shí)現(xiàn)增刪改查功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
JavaScript 監(jiān)聽textarea中按鍵事件
不同的瀏覽器中的事件監(jiān)聽機(jī)制是不同的,以onKeyPress事件為例。2009-10-10
基于JS實(shí)現(xiàn)禁止查看源碼及獲取鍵盤的按鍵值
這篇文章主要介紹了基于JS實(shí)現(xiàn)禁止查看源碼及獲取鍵盤的按鍵值,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02
JavaScript大數(shù)相加相乘的實(shí)現(xiàn)方法實(shí)例
這篇文章主要給大家介紹了關(guān)于JavaScript大數(shù)相加相乘的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
JavaScript顯示當(dāng)然日期和時(shí)間即年月日星期和時(shí)間
使用js顯示當(dāng)然日期和時(shí)間在網(wǎng)頁中很是常見,方法有很多,不過多說大同小異,下面有個(gè)不錯(cuò)的示例,需要的朋友可以感受下2013-10-10

