原型继承和类式继承
?
類式繼承(構(gòu)造函數(shù)) JS中其實(shí)是沒有類的概念的,所謂的類也是模擬出來(lái)的。特別是當(dāng)我們是用new 關(guān)鍵字的時(shí)候,就使得“類”的概念就越像其他語(yǔ)言中的類了。類式繼承是在函數(shù)對(duì)象內(nèi)調(diào)用父類的構(gòu)造函數(shù),使得自身獲得父類的方法和屬性。call和apply方法為類式繼承提供了支持。通過改變this的作用環(huán)境,使得子類本身具有父類的各種屬性。 var?father =?function() { this.age = 52; this.say =?function() { alert('hello i am '+?this.name?' and i am '+this.age +?'years old'); } } var?child =?function() { this.name =?'bill'; father.call(this); } var?man =?new?child(); man.say(); 原型繼承 繼承不在對(duì)象本身,而在對(duì)象的原型上(prototype)。 每一個(gè)對(duì)象都有原型,在瀏覽器中它體現(xiàn)在一個(gè)隱藏的__proto__屬性上。在一些現(xiàn)代瀏覽器中你可以更改它們。 當(dāng)一個(gè)對(duì)象需要調(diào)用某個(gè)方法時(shí),它會(huì)去最近的原型上查找該方法,如果沒有找到,它會(huì)再次這樣逐級(jí)查找,一直找到了要找的方法。 這些查找的原型構(gòu)成了該對(duì)象的原型鏈。原型最后指向的是null。 原型繼承,就是將父對(duì)像的方法給子類的原型。子類的構(gòu)造函數(shù)中不擁有這些方法和屬性。 var?father =?function() { } father.prototype.a =?function() { } var?child =?function(){} //開始繼承 child.prototype =?new?father(); var?man =?new?child(); man.a(); 可以看到第七行實(shí)現(xiàn)了原型繼承。很多人并不陌生這種方式。通過在瀏覽器中打印man我們就可以查看各個(gè)原型的繼承關(guān)系。 可以看到逐級(jí)的關(guān)系child->object(father實(shí)例化的對(duì)象)->father。child是通過中間層繼承了father的原型上的東西的。但是為什么中間還有一層object呢,為什么不把child.prototype = father.prototype。答案是如果這樣做child和father就沒有區(qū)別了。大家應(yīng)該還記得在prototype中有個(gè)constructor屬性,指向的是構(gòu)造函數(shù)。按照正常的情況我們要把constructor的值改回來(lái)指向child的構(gòu)造函數(shù)。但如果直接把father.prototype賦值給child.prototype,那么constructor應(yīng)該指向誰(shuí)呢?所以很顯然只能通過中間層才能使得child和father保持為獨(dú)立的對(duì)象。 對(duì)比 和原型對(duì)比起來(lái),構(gòu)造函數(shù)(類)式繼承有什么不一樣呢?首先,構(gòu)造函數(shù)繼承的方法都會(huì)存在父對(duì)象之中,每一次實(shí)例,都回將funciton保存在內(nèi)存中,這樣的做法毫無(wú)以為會(huì)帶來(lái)性能上的問題。其次類式繼承是不可變的。在運(yùn)行時(shí),無(wú)法修改或者添加新的方法,這種方式是一種固步自封的死方法。而原型繼承是可以通過改變?cè)玩溄佣鴮?duì)子類進(jìn)行修改的。另外就是類式繼承不支持多重繼承,而對(duì)于原型繼承來(lái)說(shuō),你只需要寫好extend對(duì)對(duì)象進(jìn)行擴(kuò)展即可。 組合模式 另外的一種模式,是結(jié)合類繼承和原型繼承的各自優(yōu)點(diǎn)來(lái)進(jìn)行對(duì)父類的繼承。用類式繼承屬性,而原型繼承方法。這種模式避免了屬性的公用,因?yàn)橐话銇?lái)說(shuō),每一個(gè)子類的屬性都是私有的,而方法得到了統(tǒng)一。這種模式稱為組合模式,也是繼承類式常用到的一種方法。 function?father() { this.a =?'father'?? } father.prototype.b =?function() { alert(this.a) } var?child =?function() { father.call(this) } child.prototype =?new?father(); new 關(guān)鍵字和Obeject.create方法 在文章中,博主指出了使用new關(guān)鍵字的弊端。他說(shuō):“new關(guān)鍵字掩蓋了Javascript中真正的原型繼承,使得它更像是基于類的繼承。其實(shí)new關(guān)鍵字只是Javascript在為了獲得流行度而加入與Java類似的語(yǔ)法時(shí)期留下來(lái)的一個(gè)殘留物”。作者推薦我們使用Object.create方法創(chuàng)建或者實(shí)例化對(duì)象。露珠做過測(cè)試,使用new和使用object.create方法都是將對(duì)象添加到原型上去。我們可以看一下代碼: var?father =?function() { this.a =?'father'?? } father.prototype.b =?function() {alert(this.a)} var?obj =?new?father(); 在瀏覽器中打印obj,可以觀察它的結(jié)構(gòu)。它本身是一個(gè)對(duì)象,有自身屬性,同時(shí)在其__proto__熟悉上也有b方法。在__proto__的后面有father,可以看出原型是自father來(lái)的。 那么objcet.create方法呢,我們也可以通過下面代碼測(cè)試之: var?father = { a:?'father', b:?function() { alert(this.a); } } var?obj = Object.create(father); console.dir(obj) 下面是瀏覽器輸出的結(jié)果: 可以看到,用create的方法構(gòu)造出來(lái)的對(duì)象,a屬性和b方法都是在對(duì)象的原型上,也就是說(shuō)我們可以通過更改father的屬性動(dòng)態(tài)改變obj的原型上的方法和屬性,而上面通過new關(guān)鍵字用構(gòu)造函數(shù)生成的實(shí)例,a屬性是無(wú)法改變的。從這里,我們也可以看到類繼承和原型基礎(chǔ)的一些區(qū)別。 結(jié)論 原型繼承比較符合js這種語(yǔ)言的特點(diǎn)。因?yàn)樗旧砭褪莏s強(qiáng)大的原型的一部分。而類式繼承,與其稱它為繼承方式,毋寧說(shuō)是一種函數(shù)的運(yùn)用技巧來(lái)模擬繼承罷了。本文是鹵煮的一己之見,錯(cuò)誤偏頗在所難免,如果有之,請(qǐng)各位斧正。轉(zhuǎn)載于:https://www.cnblogs.com/iOS-mt/p/11121186.html
總結(jié)
- 上一篇: 20190702 论回去
- 下一篇: C# .NET访问Oracle的三种方法