深入JS繼承
對于靈活的js而言,繼承相比于java等語言,繼承實現(xiàn)方式可謂百花齊放。方式的多樣就意味著知識點繁多,當然也是面試時繞不開的點。撇開ES6 class不談,傳統(tǒng)的繼承方式你知道幾種?每種實現(xiàn)原理是什么,優(yōu)劣點能談談嗎。這里就結(jié)合具體例子,按照漸進式的思路來看看繼承的發(fā)展。
準備談到js繼承之前先回顧下js 實例化對象的實現(xiàn)方式。
構(gòu)造函數(shù)是指可以通過new 來實例化對象的函數(shù),目的就是為了復用,避免每次都手動聲明對象實例。
new 簡單實現(xiàn)如下:
function my_new(func){ var obj = {} obj._proto_ = func.prototype // 修改原型鏈指向,拼接至func原型鏈 func.call(obj) // 實例屬性賦值 return obj}
由上可以看出,通過構(gòu)造函數(shù)調(diào)用,可以將實例屬性賦值到目標對象上。
如此可以推想,子類中調(diào)用父類構(gòu)造函數(shù)同樣可以達到繼承的目的。
這就提供了js繼承的一種思路,即通過構(gòu)造函數(shù)調(diào)用。
至于原型屬性,就是通過修改原型指向,來實現(xiàn)原型屬性的共享。
那么繼承時同樣也可以通過該方式進行。
總結(jié)基于構(gòu)造函數(shù)和原型鏈兩種特性,結(jié)合js語言的靈活性。
繼承的實現(xiàn)方式雖然繁多萬變也不離其宗
繼承的n種方式原型式繼承定義:這種繼承借助原型并基于已有的對象創(chuàng)建新對象,同時還不用創(chuàng)建自定義類型的方式稱為原型式繼承。
直接看代碼更清晰:
function createObj(o) { function F() { } F.prototype = o; return new F();}var parent = { name: ’trigkit4’, arr: [’brother’, ’sister’, ’baba’]};var child1 = createObj(parent);
該方式表面上看基于對象創(chuàng)建,不需要構(gòu)造函數(shù)(當然實際構(gòu)造函數(shù)被封裝起來罷了)。只借助了原型對象,所以名稱為原型式繼承。
缺點:
比較明顯優(yōu)良者
無法復用該繼承,每個子類的實例,都要走完整的createObj流程。
對于子類對象
因為構(gòu)造函數(shù)封裝createObj中,對其而言,沒有構(gòu)造函數(shù)。由此造成無法初始化時傳參。補充:其中 createObj 就是我們ES6中常用的Object.create(),不過Object.create進行了完善,允許額外參數(shù)來完善了。
解決思路:
既然提到?jīng)]有構(gòu)造函數(shù)導致了問題,那么大膽猜測,更進一步就是涉及了構(gòu)造函數(shù)的原型鏈繼承了。
原型鏈式繼承定義:為了讓子類繼承父類的屬性(也包括方法),首先需要定義一個構(gòu)造函數(shù)。然后,將父類的新實例賦值給構(gòu)造函數(shù)的原型。
function Parent() { this.name = ’mike’;}function Child() { this.age = 12;}Child.prototype = new Parent();child.prototype.contructor = child // 原型屬性被覆蓋,所以要修正回來。var child1 = new Child();
也就是直接修改子類的原型對象指父構(gòu)造函數(shù)的實例,這樣把父類的實例屬性和原型屬性都掛到自己原型鏈上。
缺點
Child.prototype = new Parent() ,那么子函數(shù)自身的原型屬性就被覆蓋了,如果需要就要在后面補充。
子對象實例化時,無法向父類構(gòu)造函數(shù)傳遞參數(shù)。例如在new Child()執(zhí)行的時候,想要去覆蓋name,只能在Child.prototype = new Parent()時。 是我們在new Child()的時候統(tǒng)一傳參初始化是更常規(guī)需求。
解決思路
如何在子類初始化時,調(diào)用父類構(gòu)造函數(shù)。結(jié)合前面的基礎,答案也呼之欲出。
借用構(gòu)造函數(shù)(類式繼承)類式繼承:是在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型的構(gòu)造函數(shù)。
思路比較清晰,由問題驅(qū)動。
既然原型鏈式子類不能向父類傳參的問題,那么在子類初始化是調(diào)用父類不就滿足目的了。
示例如下:
function Parent(age) { this.name = [’mike’, ’jack’, ’smith’]; this.age = age;}Parent.prototype.run = function () { return this.name + ’ are both’ + this.age;};function Child(age) { // 調(diào)用父類 Parent.call(this, age);}var child1 = new Child(21);
這樣滿足了初始化時傳參的需求,但是問題也比較明顯。
child1.run //undefined
問題
父類原型屬性丟失
父類初始化只繼承了示例屬性,原型屬性在子類的原型鏈上丟失
解決思路
丟失的原因在于原型鏈沒有修改指向,那么修改下指向不就完了。
組合繼承定義:使用原型鏈實現(xiàn)對原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承
示例:
function Parent(age) { this.name = [’mike’, ’jack’, ’smith’]; this.age = age;}Parent.prototype.run = function () { return this.name + ’ are both’ + this.age;};function Child(age) { // 調(diào)用父類構(gòu)造函數(shù) Parent.call(this, age);}Child.prototype = new Parent();//原型屬性繼承Child.prototype.contructor = Childvar child1 = new Child(21);
這樣問題就避免了:
child1.run() // 'mike,jack,smith are both21'
問題
功能滿足之后,就該關注性能了。這種繼承方式問題在于父類構(gòu)造函數(shù)執(zhí)行了兩次。
分別是:
function Child(age) { // 調(diào)用父類構(gòu)造函數(shù),第二次 Parent.call(this, age);}Child.prototype = new Parent();//修改原型鏈指向,第一次
解決思路
解決自然是取消一次構(gòu)造函數(shù)調(diào)用,要取消自然要分析這兩次執(zhí)行,功能上是否有重復。
第一次同樣繼承了實例和原型屬性,第二次執(zhí)行同樣繼承了父類的實例屬性。
因此第二次滿足對父類傳參的不可獲取性,因此只能思考能否第一次不調(diào)用父類構(gòu)造函數(shù),只繼承原型屬性。
答案自然是能,前面原型式繼承就是這個思路。
寄生組合式繼承顧名思義,寄生指的是將繼承原型屬性的方法封裝在特定方法中,組合的是將構(gòu)造函數(shù)繼承組合起來,補充原型式繼承的不足。
饒了點,直接看:
function createObj(o) { function F() { } F.prototype = o; return new F();}//繼承原型屬性 即原型式繼承function create(parent, child) { var f = createObj(parent.prototype);//獲取原型對象 child.prototype = f child.prototype.constructor = child;//增強對象原型,即保持原有constructor指向}function Parent(name) { this.name = name; this.arr = [’brother’, ’sister’, ’parents’];}Parent.prototype.run = function () { return this.name;};function Child(name, age) { // 示例屬性 Parent.call(this, name); this.age = age;}// 原型屬性繼承寄生于該方法中create(Parent.prototype,Child);var child1 = new Child(’trigkit4’, 21);
這樣沿著發(fā)現(xiàn)問題解決問題的思路直到相對完善的繼承方式。至于ES的方式本篇就不涉及了。
結(jié)束語唯有厚積,才能薄發(fā),想要心儀的offer,就得準備充裕,夯實基礎,切忌似是而非,道理我都懂就是答得不完全,這樣跟不懂差別也不太大。不算新的日子里立個flag,每周三個知識點回顧。要去相信,你若盛開蝴蝶自來。
以上就是深入JS繼承的詳細內(nèi)容,更多關于深入JS繼承的資料請關注好吧啦網(wǎng)其它相關文章!
相關文章:
1. python實現(xiàn)感知機模型的示例2. Python unittest單元測試框架實現(xiàn)參數(shù)化3. .net core 中 WebApiClientCore的使用示例代碼4. springboot多模塊包掃描問題的解決方法5. Android Studio編寫AIDL文件后如何實現(xiàn)自動編譯生成6. iOS UIScrollView和控制器返回手勢沖突解決方法7. 深入淺出 妙用Javascript中apply、call、bind8. windows下安裝PHP性能分析工具 xhprof 筆記9. 基于vue實現(xiàn)探探滑動組件功能10. 利用google trends來觀察一下Java技術(shù)的發(fā)展趨勢
