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