首先我們要知道繼承是要做什么的, 是為了節(jié)省內(nèi)存消耗, 避免重復(fù)調(diào)用問(wèn)題
warning
本篇文章建立在已經(jīng)有所了解JavaScript繼承的情況下去進(jìn)行進(jìn)一步梳理(原創(chuàng)
)
簡(jiǎn)述
并且既然叫做JavaScript繼承, 那么這幾個(gè)問(wèn)題都是圍繞怎樣實(shí)現(xiàn)繼承
用抄襲試卷的方式來(lái)講解,
原型相當(dāng)于答案的最開(kāi)始來(lái)源處
constructor相當(dāng)于最后的簽名
你總不能把別人的名字一起抄上去吧, 這代表簽名, 這份試卷是你自己的
如果我要實(shí)時(shí)了解你的試卷, 那我就要指定原型是你, 相當(dāng)于構(gòu)建一個(gè)單向偷窺隧道
當(dāng)然如果也有其他人偷窺你的試卷, 那么我和那個(gè)人就是共享原型, 原型是你的試卷
1. 構(gòu)造函數(shù)繼承
(繼承構(gòu)造函數(shù)
), 也叫類抄寫(xiě), 比如我們?cè)诳荚? 你寫(xiě)了一個(gè)構(gòu)造函數(shù), 我要繼承你的構(gòu)造函數(shù), 就在我的構(gòu)造函數(shù)里面用call或者cpply抄寫(xiě)一份你的東西, 我給你題目你給我答案
, 當(dāng)然我抄完后還可以在自己的構(gòu)造函數(shù)的選做題
里面放一些自己的東西
因?yàn)槲抑皇浅四愕臇|西, 但是我們并不是公用一份, 所以, 我們兩個(gè)構(gòu)造函數(shù)是獨(dú)立的
缺點(diǎn)是, 因?yàn)椴皇枪蚕? 是一次性拷貝
, 也就是如果你后面修改了你的答案我是不知道的, 除非我再抄你一遍
2. 原型鏈繼承
也叫類繼承(也是繼承構(gòu)造函數(shù)
), 最簡(jiǎn)單的繼承, 比如老大是原型, 你是二當(dāng)家, 其他人都是小弟都叫實(shí)例繼承了二當(dāng)家, 當(dāng)然默認(rèn)也是繼承了老大
, 我們這個(gè)幫派一旦老大或者二當(dāng)家有政策發(fā)布, 下面的小弟都要修改
這就是原型鏈繼承,
缺點(diǎn)是, 上級(jí)改動(dòng), 下級(jí)都要跟著修改
3. 組合繼承
(繼承構(gòu)造函數(shù)
), 什么意思, 我們?cè)跇?gòu)造函數(shù)基礎(chǔ)的基礎(chǔ)上再進(jìn)行講述
我還是要抄你試卷, 首先我還是要全抄!!!
, 因?yàn)槟闶菍W(xué)霸
但是我不想只抄一遍, 你有些大題改了我也要知道, 所以你就把你的試卷分成兩部分, 一部分是給我給你題目你給我對(duì)于的答案, 這部分放在構(gòu)造函數(shù)內(nèi)
, 你可能要修改的部分改了要告訴我, 你就把答案放在你的原型里面
你改了我也改
當(dāng)然我也可以有自己的想法, 可以加一些自己的答案
缺點(diǎn): 調(diào)用了兩次父類構(gòu)造函數(shù), 耗費(fèi)內(nèi)存, 第一次是讓你幫我做題, new一個(gè)你的構(gòu)造函數(shù)返回給我, 目的是為了成為你的實(shí)例好去引用你的原型
使用 new 操作符調(diào)用了一次 構(gòu)造函數(shù)
有方法避免多次調(diào)用直接去掉 new 操作符
寫(xiě)成 Child.prototype = Parent.prototype
但是這樣并不好,雖然避免出現(xiàn)重復(fù)調(diào)用但導(dǎo)致修改子類 constructor 的時(shí)候父類也被修改了, 也就是
如果我再試卷上簽了自己的名字
那么意味著你的原型的構(gòu)造者不在是你的構(gòu)造函數(shù), 而是我的了
這是組合式繼承的唯一缺點(diǎn)
4. 原型式繼承(繼承對(duì)象
) ———————注意是對(duì)象
用es5的object.create(對(duì)象)返回一個(gè)實(shí)例給我抄
原理是淺拷貝
我直接抄襲你的對(duì)象, 我不想改, 沒(méi)有想法
抄襲對(duì)象的話不用簽名的, 也就是不用constructor
5. 寄生式繼承(繼承對(duì)象
)———————注意是對(duì)象
原型式繼承的基礎(chǔ)上為子類增加屬性和方法, 就是繼承了對(duì)方也添加了自己的方法
6. 寄生組合式繼承
解決了之前組合繼承的兩次調(diào)用父類構(gòu)造函數(shù)問(wèn)題
那么第一次調(diào)用父類構(gòu)造函數(shù)是不可避免的, 就是parent.call(this)這個(gè)是不可省略的
要精簡(jiǎn)的是組合式繼承的第二次通過(guò)調(diào)用new Parent()
想辦法來(lái)解決這次的調(diào)用
之前說(shuō)過(guò)不用new,改用parent.prototype來(lái)公用同一個(gè)原型是不合理的
因?yàn)槿绻业脑偷腸onstructor指向了我自己的構(gòu)造函就會(huì)導(dǎo)致原型的constructor也會(huì)改成我的構(gòu)造函數(shù), 就不合理了, 因?yàn)樵偷臉?gòu)造函數(shù)還是要指向我的父類的
那么就可以用到原型式繼承
的解決方案來(lái)解決, 即在原型中間新建一個(gè)空的構(gòu)造函數(shù)作為中介, 然后如果我執(zhí)行child.prototype.constructor=child就不會(huì)影響parent.constructor指向我會(huì)變成空構(gòu)造函數(shù)的構(gòu)造者變成了我
從而解決了問(wèn)題
7. es6的calss繼承(推薦使用
)
挺優(yōu)秀的方式, 推薦使用, 語(yǔ)法很清晰
class Parent {
constructor(name, friends) { // 該屬性在構(gòu)造函數(shù)上宾毒,不共享
this.name = name
this.friends = friends
}
log() { // 該方法在原型上塑顺,共享
return this
}
}
Parent.prototype.share = [1, 2, 3] // 原型上的屬性固以,共享
class Child extends Parent {
constructor(name, friends, gender) {
super(name, friends) // 繼承父類構(gòu)造函數(shù)
this.gender = gender
}
}