摘要:借用構(gòu)造函數(shù)繼承針對上面的繼承方法的缺點(diǎn),開發(fā)人員使用一種叫做借用構(gòu)造函數(shù)的技術(shù),也就是我們平時(shí)說的跟繼承。
繼承是 OO 語言中一個(gè)最為津津樂道的概念,許多 OO 語言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。由于函數(shù)沒有簽名,在 ECMAScript 中無法實(shí)現(xiàn)接口繼承。ECMAScript 只支持實(shí)現(xiàn)繼承而且實(shí)現(xiàn)繼承主要是依靠原型鏈來實(shí)現(xiàn)的。
關(guān)于原型鏈,我之前的文章里面有介紹,如果有些忘記了,可以看這篇文章。
下面我將詳細(xì)的介紹前端前輩在開發(fā)過程中不斷摸索創(chuàng)造的幾種繼承方式??赐昝嬖嚨臅r(shí)候千萬不要簡單的回答 call 跟 apply 了。
為了說起來省事,雖然 js 沒有嚴(yán)格意義的類,我還是以父類和子類來做區(qū)分繼承關(guān)系。
既然子類想要繼承父類的全部方法,而且我們知道父類的實(shí)例擁有父類所有的方法,那么接下類就好辦了,我將子類的 prototype 指向父類的實(shí)例,子類就擁有了父類的全部方法了
// 定義父類 function Parent (name, age) { this.name = name; this.age = age; } Parent.prototype.sayName = function () { alert(this.name); } // 定義子類 function Child (sex) { this.sex = sex; } // 實(shí)現(xiàn)繼承 var p = new Parent("leizore", 25); Child.prototype = p; var child = new Child("男"); child.sayName(); // leizore
那么對應(yīng)的關(guān)系圖如下:
這種方式 Child 繼承了 Person 的全部方法,但是也是有缺點(diǎn)的。
創(chuàng)建子類實(shí)例時(shí),無法向父類構(gòu)造函數(shù)傳參。指定 prototype 時(shí),實(shí)例化 Person 傳的參數(shù),會出現(xiàn)在所有子類上,不靈活。
由圖可以看到,p 的 contructor 指向 Person, 所以 Child.prototype.constructor 也指向 Person,顯然會導(dǎo)致繼承鏈的紊亂。
2.借用構(gòu)造函數(shù)繼承針對上面的繼承方法的缺點(diǎn)1,開發(fā)人員使用一種叫做借用構(gòu)造函數(shù)的技術(shù),也就是我們平時(shí)說的 call 跟 apply 繼承。
// 定義父類 function Parent (name, age) { this.name = name; this.age = age; } Parent.prototype.sayName = function () { alert(this.name); } // 定義子類 function Child (name, age, sex) { // 繼承,同時(shí)傳遞了參數(shù) Parent.call(this, name, age) this.sex = sex; }
這里簡單講一下 call(apply)是如何實(shí)現(xiàn)的,其實(shí)就是將 call(apply) 前面的函數(shù)立即執(zhí)行一遍,并且執(zhí)行時(shí)將作用域 this 指向 call(apply) 函數(shù)的第一個(gè)參數(shù),比如這里的 call 就是將 Parent 實(shí)例一遍,將 name 跟 age 當(dāng)成參數(shù)傳過去
這種繼承方式解決了繼承過程中的傳參問題,但是缺點(diǎn)是并沒有繼承到父類的原型,為了解決這個(gè)問題,我們很容易想到將上面兩個(gè)方法結(jié)合起來不久好了。于是另一種繼承方式出現(xiàn)了
沒錯(cuò),就是兩種方式并用,從而發(fā)揮兩者之長的一種繼承模式,代碼如下
// 定義父類 function Parent (name, age) { this.name = name; this.age = age; } Parent.prototype.sayName = function () { alert(this.name); } // 定義子類 function Child (name, age, sex) { // 繼承,同時(shí)傳遞了參數(shù) Parent.call(this, name, age) this.sex = sex; } Child.prototype = new Parent("leizore", 25);
嗯,這種方式基本上解決了開發(fā)過程中繼承的痛點(diǎn),成為好多人常用的繼承模式之一。但是缺點(diǎn)也是有的
重復(fù)定義了屬性,可以看到將 Child 的 prototype指向 Perent 的實(shí)例時(shí),繼承了name 跟 age 屬性,實(shí)例 Child 的時(shí)候,調(diào)用 call 函數(shù),又繼承了一次,雖然使用 call 調(diào)用這次的屬性是在實(shí)例屬性上,當(dāng)獲取name時(shí)優(yōu)先返回實(shí)例屬性,然后在 prototype 上,所以并不會出大問題。
第一種繼承方式方式的缺點(diǎn)二也完美的繼承過來了,Child.prototype.constructor 還是指向 parent
那么肯定有人會說,既然Child.prototype.constructor 不指向自己,那么直接讓他指向自己不就好了?
Child.prototype.constructor = Child;
答案是不行的。因?yàn)?Child.prototype 是 Parent 的實(shí)例,這樣操作會將 Parent.prototype.constructor 也指向 Child,顯然也是不合理的。
4.原型式繼承為了解決上面 Child 與 Parent 繼承之后糾纏不清的問題,道格拉斯在2006年提出一種繼承方法,它的想法是借助原型可以給予已有的對象創(chuàng)建新對象,同時(shí)還不必因此創(chuàng)建自定義類型。函數(shù)如下
function object (o) { function F() {} F.prototype = o; return new F(); }
這個(gè)模式相當(dāng)與創(chuàng)建一個(gè)新的對象,對象繼承了o所有屬性,當(dāng)然這里也只是實(shí)現(xiàn)了淺拷貝。
5.組合寄生式繼承嗯,想必大家也想到了,上面這種繼承方式可以解決 Child 與 Parent 繼承后的糾纏不清的關(guān)系??梢杂?object 方法創(chuàng)建一個(gè)臨時(shí)對象,從而斬?cái)喔?Parent 的聯(lián)系。就可以放心的對 Child 原型的constructor 隨便指了,當(dāng)然了為了繼承鏈的不紊亂,還是指向自己比較好
// 定義父類 function Parent (name, age) { this.name = name; this.age = age; } Parent.prototype.sayName = function () { alert(this.name); } // 定義子類 function Child (name, age, sex) { // 繼承,同時(shí)傳遞了參數(shù) Parent.call(this, name, age) this.sex = sex; } function object (o) { function F() {} F.prototype = o; return new F(); } var prototype = object(Parent.prototype); prototype.constructor = Child; Child.prototype = prototype; var c = new Child("leizore", 11, "men"); c.sayName() // leizore c.constructor === Child // true
到此,基本上解決了上面所說的所有缺點(diǎn)。當(dāng)然了,也是有一點(diǎn)問題的,就是方法四的實(shí)現(xiàn)其實(shí)是淺拷貝,如果 Parent.prototype 里又引用類型比如數(shù)組,對象,改變Parent.prototype,Child 也會跟著變,解決方式也很簡單,使用深拷貝就行了,同時(shí)又可以寫很多繼承方式。當(dāng)然了,按照我上面順下來的思想,也可以寫出自己的繼承方式
比如下面改變object函數(shù):
// 定義父類 function Parent (name, age) { this.name = name; this.age = age; } Parent.prototype.sayName = function () { alert(this.name); } // 定義子類 function Child (name, age, sex) { // 繼承,同時(shí)傳遞了參數(shù) Parent.call(this, name, age) this.sex = sex; } function object (o) { var c = {}; for (var i in o) { c[i] = o[i]; } return c } var prototype = object(Parent.prototype); prototype.constructor = Child; Child.prototype = prototype; var c = new Child("leizore", 11, "men"); c.sayName() // leizore c.constructor === Child // true
當(dāng)然了,es6 中,可以通過extends關(guān)鍵字實(shí)現(xiàn)繼承,這里就不多說了
參考javascript 高級程序設(shè)計(jì)
Javascript面向?qū)ο缶幊蹋ǘ簶?gòu)造函數(shù)的繼承
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95660.html
摘要:實(shí)際上也就是在原型鏈繼承的代碼中添加在子類的構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù)。寄生組合式繼承在指定子類的原型的時(shí)候不必調(diào)用父類的構(gòu)造函數(shù),而是直接使用創(chuàng)建父類原型的副本。 原本地址:http://www.ahonn.me/2017/01/2... 眾所周知,JavaScript 的繼承是實(shí)現(xiàn)繼承,而沒有 Java 中的接口繼承。這是因?yàn)?JavaScript 中函數(shù)沒有簽名,而實(shí)現(xiàn)繼承依靠的...
摘要:前言面試中對于技術(shù)職位,一般分筆試與面談,如果面試官的一些小問題你可以立馬找到對應(yīng)的知識點(diǎn)擴(kuò)展開來,那么這就是你的優(yōu)勢,本系列將講述一些面試中的事,不會很詳細(xì),但是應(yīng)該比較全面吧。 前言 面試中對于技術(shù)職位,一般分筆試與面談,如果面試官的一些小問題你可以立馬找到對應(yīng)的知識點(diǎn)擴(kuò)展開來,那么這就是你的優(yōu)勢,本系列將講述一些java面試中的事,不會很詳細(xì),但是應(yīng)該比較全面吧。 主要內(nèi)容 pa...
摘要:有需要還可以修改指向謙龍寄生組合式繼承思路是通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混合形式來繼承方法改變執(zhí)行環(huán)境實(shí)現(xiàn)繼承有需要還可以修改指向謙龍謙龍拷貝繼承該方法思路是將另外一個(gè)對象的屬性和方法拷貝至另一個(gè)對象使用遞歸 前言 js中實(shí)現(xiàn)繼承的方式只支持實(shí)現(xiàn)繼承,即繼承實(shí)際的方法,而實(shí)現(xiàn)繼承主要是依靠原型鏈來完成的。 原型鏈?zhǔn)嚼^承 該方式實(shí)現(xiàn)的本質(zhì)是重寫原型對象,代之以一個(gè)新類型的實(shí)例...
摘要:格式子類名父類名好處提高了代碼的復(fù)用性提高了代碼的維護(hù)性通過少量的修改,滿足不斷變化的具體要求讓類與類產(chǎn)生了一個(gè)關(guān)系,是多態(tài)的前提要求有共同的屬性或操作有細(xì)微的差別繼承的弊端讓類的耦合性增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000019321816?w=600&h=242); 第二階段 JAVA面向?qū)ο?第二章 繼承 其...
摘要:通過同一個(gè)構(gòu)造函數(shù)實(shí)例化的多個(gè)實(shí)例對象具有同一個(gè)原型對象。所以當(dāng)給原型對象賦值一個(gè)新對象時(shí),切記將原型對象的指回原構(gòu)造函數(shù)以上就是本次分享的內(nèi)容,關(guān)于原型對象的其他知識,下一篇基礎(chǔ)原型對象的那些事二會講到。 談起js的基礎(chǔ),繞不過去的坎就是:原型鏈、作用域鏈、this(em...好吧,還有閉包),今天總結(jié)一下關(guān)于原型對象的一些知識,供自己和大家復(fù)習(xí)。 概念理解 什么是原型對象呢?有以下...
閱讀 3837·2021-11-17 09:33
閱讀 2049·2021-10-26 09:51
閱讀 1573·2021-09-29 09:44
閱讀 1715·2019-08-30 15:55
閱讀 1473·2019-08-30 15:52
閱讀 2355·2019-08-30 15:43
閱讀 3459·2019-08-29 17:00
閱讀 2328·2019-08-29 16:23