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)存,(對(duì)于多個(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,將其掛載到對(duì)應(yīng)的原型上,達(dá)到了在繼承的基礎(chǔ)上進(jìn)一步擴(kuò)充自身的目的。
這種繼承方式很明顯就是采用類似于傳統(tǒng)面向?qū)ο笳Z言的類式繼承,優(yōu)點(diǎn)是對(duì)習(xí)慣于傳統(tǒng)面向?qū)ο蟾拍畹某绦騿T來講很容易理解,缺點(diǎn)是過程比較繁瑣,內(nèi)存消耗稍大,因?yàn)樽宇愐矒碛凶约旱臉?gòu)造函數(shù)及原型,而且子類和父類的屬性完全是隔離的,即使兩者是一樣的值,但是不能共享同一段內(nèi)存。
方法二:原型式繼承
首先定義一個(gè)父類,這里不會(huì)刻意模仿使用構(gòu)造函數(shù)來定義,而是直接采用對(duì)象字面量的方式定義一個(gè)對(duì)象,該對(duì)象就是父類
var Person={ name:'default name', getName:function(){ return this.name; } } ;
和第一種方法一樣,該對(duì)象擁有一個(gè)屬性name和一個(gè)方法getName .
然后再定義一個(gè)克隆方法,用來實(shí)現(xiàn)子類對(duì)父類的繼承,如下:
function clone(obj){ function F(){} F.prototype=obj; return new F(); }
該克隆方法新建一個(gè)對(duì)象,將該對(duì)象的原型指向父類即參數(shù)obj,同時(shí)返回這個(gè)對(duì)象。
最后子類再通過克隆函數(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屬性值。
這種原型式繼承相比于類式繼承更為簡(jiǎn)單自然,同時(shí)如果子類的屬性和父類屬性值相同,可以不進(jìn)行修改的話,那么它們兩者其實(shí)共享的是同一段內(nèi)存空間,如上面的name屬性,缺點(diǎn)是對(duì)于習(xí)慣了傳統(tǒng)面向?qū)ο蟮某绦騿T難以理解,如果兩者要進(jìn)行選擇的話,無疑是這種方式更為優(yōu)秀一些。
既然javascript中采用基于原型的方式來實(shí)現(xiàn)繼承,而且每個(gè)對(duì)象的原型只能指向某個(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中使用stopPropagation函數(shù)停止事件傳播例子
這篇文章主要介紹了JavaScript中使用stopPropagation函數(shù)停止事件傳播例子,即阻止事件冒泡的一個(gè)方法,需要的朋友可以參考下2014-08-08JavaScript實(shí)現(xiàn)圖片瀑布流和底部刷新
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)圖片瀑布流和底部刷新,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01javascript 網(wǎng)站常用的iframe分割
就是一個(gè)頁面使用兩個(gè)iframe來調(diào)用內(nèi)容,實(shí)現(xiàn)頁面導(dǎo)航,更容易控制,可控制性好2008-06-06JavaScript中常用的3種彈出提示框(alert、confirm、prompt)
這篇文章主要給大家介紹了關(guān)于JavaScript中常用的3種彈出提示框(alert、confirm、prompt)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11JavaScript flash復(fù)制庫類 Zero Clipboard
開發(fā)中經(jīng)常會(huì)用到復(fù)制的功能,在 IE 下實(shí)現(xiàn)比較簡(jiǎn)單。但要想做到跨瀏覽器比較困難了。2011-01-01js簡(jiǎn)單實(shí)現(xiàn)圖片延遲加載的方法
這篇文章主要介紹了js簡(jiǎn)單實(shí)現(xiàn)圖片延遲加載的方法,涉及javascript針對(duì)頁面元素的遍歷與動(dòng)態(tài)設(shè)置技巧,需要的朋友可以參考下2016-07-07用js的document.write輸出的廣告無阻塞加載的方法
這篇文章主要介紹了用js的document.write輸出的廣告無阻塞加載的方法,需要的朋友可以參考下2014-06-06