JavaScript中實現(xiàn)繼承的三種方式和實例
javascript雖然是一門面向?qū)ο蟮恼Z言,但是它的繼承機(jī)制從一開始設(shè)計的時候就不同于傳統(tǒng)的其他面向?qū)ο笳Z言,是基于原型的繼承機(jī)制,但是在這種機(jī)制下,繼承依然有一些不同的實現(xiàn)方式。
方法一:類式繼承
所謂的類式繼承就是指模仿傳統(tǒng)面向?qū)ο笳Z言的繼承方式,繼承與被繼承的雙方都是“類”,代碼如下:
首先定義一個父類(或超類):
function Person(name){ this.name=name; } Person.prototype.getName=function(){ return this.name; };
該父類person的屬性在構(gòu)造函數(shù)中定義,可以保證繼承它的子類的name屬性不與它共享這個屬性,而是單獨屬于子類,將getName方法掛載到原型上,是為了讓繼承它的子類的多個實例共享這一個方法體,這樣能夠節(jié)省內(nèi)存,(對于多個實例來講,每次New一個實例出來都會保證這些實例的getName方法引用的是同一段內(nèi)存空間,而不是獨立的空間)。
定義一個繼承方法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; } }
在這個方法中,首先創(chuàng)建一個新的類為F,讓它的原型為父類的原型,并且讓子類的原型指向該類F的一個實例,從而達(dá)到了一個繼承父類的目的,同時,子類的原型由于被修改,所以將修改后的原型的constructor屬性指向子類,讓其擁有構(gòu)造函數(shù),同時給該子類掛載一個superClass屬性,子類可以通過該屬性調(diào)用父類,從而建立起了子類和父類的關(guān)系。
定義一個子類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ù),同時采用call方法,轉(zhuǎn)換該方法調(diào)用的this指向,讓子類Author也擁有(繼承)父類的屬性,同時子類又擁有自己的屬性book,所以在構(gòu)造函數(shù)中將參數(shù)books賦值給屬性book,達(dá)到構(gòu)造的目的。采用extend函數(shù)繼承父類Person原型上的屬性和方法(實際上只繼承了方法,因為我們之前只是將方法掛載到了原型上,屬性是在構(gòu)造函數(shù)中定義的)。同時,Author又擁有自己的方法getBooks,將其掛載到對應(yīng)的原型上,達(dá)到了在繼承的基礎(chǔ)上進(jìn)一步擴(kuò)充自身的目的。
這種繼承方式很明顯就是采用類似于傳統(tǒng)面向?qū)ο笳Z言的類式繼承,優(yōu)點是對習(xí)慣于傳統(tǒng)面向?qū)ο蟾拍畹某绦騿T來講很容易理解,缺點是過程比較繁瑣,內(nèi)存消耗稍大,因為子類也擁有自己的構(gòu)造函數(shù)及原型,而且子類和父類的屬性完全是隔離的,即使兩者是一樣的值,但是不能共享同一段內(nèi)存。
方法二:原型式繼承
首先定義一個父類,這里不會刻意模仿使用構(gòu)造函數(shù)來定義,而是直接采用對象字面量的方式定義一個對象,該對象就是父類
var Person={ name:'default name', getName:function(){ return this.name; } } ;
和第一種方法一樣,該對象擁有一個屬性name和一個方法getName .
然后再定義一個克隆方法,用來實現(xiàn)子類對父類的繼承,如下:
function clone(obj){ function F(){} F.prototype=obj; return new F(); }
該克隆方法新建一個對象,將該對象的原型指向父類即參數(shù)obj,同時返回這個對象。
最后子類再通過克隆函數(shù)繼承父類,如下:
var Author=clone(Person); Author.book=['javascript']; Author.showBook=function(){ return this.book; }
這里定義一個子類,通過clone函數(shù)繼承父類Person,同時拓展了一個屬性book,和一個方法showBook,這里該子類也擁有屬性name,但是它和父類的name值是一樣的,所以沒有進(jìn)行覆蓋,如果不一樣,可以采用
Author.name='new name';覆蓋這個屬性,從而得到子類的一個新的name屬性值。
這種原型式繼承相比于類式繼承更為簡單自然,同時如果子類的屬性和父類屬性值相同,可以不進(jìn)行修改的話,那么它們兩者其實共享的是同一段內(nèi)存空間,如上面的name屬性,缺點是對于習(xí)慣了傳統(tǒng)面向?qū)ο蟮某绦騿T難以理解,如果兩者要進(jìn)行選擇的話,無疑是這種方式更為優(yōu)秀一些。
既然javascript中采用基于原型的方式來實現(xiàn)繼承,而且每個對象的原型只能指向某個特定的類的實例(不能指向多個實例),那么如何實現(xiàn)多重繼承(即讓一個類同時具有多個類的方法和屬性,而且本身內(nèi)部不自己定義這些方法和屬性)?
在javascript設(shè)計模式中給出了一種摻元類(mixin class)的方式:
首先定義一個摻元類,用來保存一些常用的方法和屬性,這些方法和屬性可以通過拓展的方式添加到任何其他類上,從而被添加類就具有了該類的某些方法和屬性,如果定義多個摻元類,同時添加給一個類,那么該類就是間接實現(xiàn)了“多重繼承”,基于這種思想,實現(xiàn)如下:
摻元類定義:
var Mixin=function(){}; Mixin.prototype={ serialize:function(){ var output=[]; for(key in this){ output.push(key+":"+this[key]); } return output.join(','); } }
該摻元類具有一個serialize方法,用來遍歷其自身,輸出自身的屬性和屬性值,并且將他們以字符串形式返回,中間用逗號隔開。
定義一個擴(kuò)充方法,用來使某個類經(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)是兩個參數(shù),第一個參數(shù)是接受被擴(kuò)充的類,第二個參數(shù)是摻元類(用來擴(kuò)充其他類的類),還可以有其他參數(shù),如果大于兩個參數(shù),后面的參數(shù)都是方法或者屬性名,用來表示被擴(kuò)充類只想繼承摻元類的指定的屬性或方法,否則默認(rèn)繼承摻元類的所有屬性和方法,在該函數(shù)中,第一個if分支是用來繼承指定屬性和方法的情形,else分支是默認(rèn)繼承所有屬性和方法的情形。該方法的實質(zhì)是將摻元類的原型上的屬性和方法都擴(kuò)充(添加)到了被擴(kuò)充類的原型上面,從而使其具有摻元類的屬性和方法。
最后,使用擴(kuò)充方法實現(xiàn)多重繼承
augment(Author,Mixin); var author= new Author('js',['javascript design patterns']); alert(author.serialize());
這里定義了一個author的類,該類繼承自Person父類,同時又具備摻元類Mixin的方法和屬性,如果你愿意,可以定義N個摻元類用來擴(kuò)充該類,它同樣能夠繼承你定義的其他摻元類的屬性和方法,這樣就實現(xiàn)了多重繼承,最后,author的serialize方法的運(yùn)行結(jié)果如下:
你會發(fā)現(xiàn)該類同時具有person類,Author類,Mixin類的屬性和方法,其中Person和Mixin的屬性和方法都是通過“繼承”得來的,從實際上來講,它實現(xiàn)了多重繼承。
相關(guān)文章
JavaScript中使用stopPropagation函數(shù)停止事件傳播例子
這篇文章主要介紹了JavaScript中使用stopPropagation函數(shù)停止事件傳播例子,即阻止事件冒泡的一個方法,需要的朋友可以參考下2014-08-08javascript 網(wǎng)站常用的iframe分割
就是一個頁面使用兩個iframe來調(diào)用內(nèi)容,實現(xiàn)頁面導(dǎo)航,更容易控制,可控制性好2008-06-06JavaScript中常用的3種彈出提示框(alert、confirm、prompt)
這篇文章主要給大家介紹了關(guān)于JavaScript中常用的3種彈出提示框(alert、confirm、prompt)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11JavaScript flash復(fù)制庫類 Zero Clipboard
開發(fā)中經(jīng)常會用到復(fù)制的功能,在 IE 下實現(xiàn)比較簡單。但要想做到跨瀏覽器比較困難了。2011-01-01用js的document.write輸出的廣告無阻塞加載的方法
這篇文章主要介紹了用js的document.write輸出的廣告無阻塞加載的方法,需要的朋友可以參考下2014-06-06