一、原型鏈繼承
構(gòu)造函數(shù)、原型和實(shí)例的關(guān)系: 每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型有一個(gè)屬性指回構(gòu)造函數(shù),而實(shí)例有一個(gè)內(nèi)部指針指向原型。
原型鏈的基本構(gòu)想: 如果原型是另一個(gè)類型的實(shí)例呢?那就意味著這個(gè)原型本身有一個(gè)內(nèi)部指針指向另一個(gè)原型,相應(yīng)地另一個(gè)原型也有一個(gè)指針指向另一個(gè)構(gòu)造函數(shù)。這樣就在實(shí)例和原型之間構(gòu)造了一條原型鏈。
重點(diǎn): 讓新實(shí)例的原型等于父類的實(shí)例。
function SuperType() {
this.property = true
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
function SubType() {
this.subproperty = false
}
// 繼承 SuperType
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
return this.subproperty
}
let instance = new SubType()
console.log(instance.getSuperValue()) // true
復(fù)制代碼
特點(diǎn):
- 實(shí)例可繼承的屬性有:實(shí)例的構(gòu)造函數(shù)的屬性,父類構(gòu)造函數(shù)屬性,父類原型的屬性。
缺點(diǎn):
- 新實(shí)例無(wú)法向父類構(gòu)造函數(shù)傳參。
- 繼承單一。(只能繼承一個(gè)父類構(gòu)造函數(shù))
- 所有新實(shí)例都會(huì)共享父類實(shí)例的屬性。(原型上的屬性是共享的,一個(gè)實(shí)例修改了原型屬性,另一個(gè)實(shí)例的原性也會(huì)被修改?。?/li>
- 要想為子類原型新增屬性和方法,必須要在new SuperType()這樣的語(yǔ)句之后執(zhí)行
代碼如下:
function SuperType() {
this.colors = ["red", "blue", "green"]
}
function SubType() {}
// 繼承 SuperType
SubType.prototype = new SuperType()
let instance1 = new SubType()
instance1.colors.push("black")
console.log(instance1.colors) // "red,blue,green,black"
let instance2 = new SubType()
console.log(instance2.colors) // "red,blue,green,black"
復(fù)制代碼
二、借用構(gòu)造函數(shù)繼承
重點(diǎn): 用.call()和.apply()將父類構(gòu)造函數(shù)引入子類函數(shù)(在子類函數(shù)中做了父類函數(shù)的自執(zhí)行(復(fù)制))
function SuperType(name) {
this.name = name
}
function SubType() {
// 繼承 SuperType 并傳參
SuperType.call(this, "Nicholas")
// 實(shí)例屬性
this.age = 29
}
let instance = new SubType()
console.log(instance.name) // "Nicholas";
console.log(instance.age) // 29
復(fù)制代碼
特點(diǎn):
- 只繼承了父類構(gòu)造函數(shù)的屬性,沒(méi)有繼承父類原型的屬性。
- 解決了原型鏈繼承缺點(diǎn) 1、2、3。
- 可以繼承多個(gè)構(gòu)造函數(shù)屬性(call 多個(gè))。
- 在子實(shí)例中可向父實(shí)例傳參。
- 解決了引用值問(wèn)題
缺點(diǎn):
- 只能繼承父類構(gòu)造函數(shù)的屬性。
- 無(wú)法實(shí)現(xiàn)構(gòu)造函數(shù)的復(fù)用。
- 每個(gè)新實(shí)例都有父類構(gòu)造函數(shù)的副本,臃腫。
三、組合繼承(組合原型鏈繼承和借用構(gòu)造函數(shù)繼承)(常用)
重點(diǎn): 結(jié)合了兩種模式的優(yōu)點(diǎn),傳參和復(fù)用
function SuperType(name) {
this.name = name
this.colors = ["red", "blue", "green"]
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age) {
// 繼承屬性
SuperType.call(this, name) //// 第一次調(diào)用 SuperType()
this.age = age
}
// 繼承方法
SubType.prototype = new SuperType() // 第二次調(diào)用 SuperType()
SubType.prototype.sayAge = function () {
console.log(this.age)
}
let instance1 = new SubType("Nicholas", 29)
console.log("instance1=>", instance1)
instance1.colors.push("black")
console.log(instance1.colors) // "red,blue,green,black"
instance1.sayName() // "Nicholas";
instance1.sayAge() // 29
let instance2 = new SubType("Greg", 27)
console.log(instance2.colors) // "red,blue,green"
instance2.sayName() // "Greg";
instance2.sayAge() // 27
復(fù)制代碼
特點(diǎn):
- 可以繼承父類原型上的屬性,可以傳參,可復(fù)用。
- 每個(gè)新實(shí)例引入的構(gòu)造函數(shù)屬性是私有的。
缺點(diǎn): 組合繼承其實(shí)也存在效率問(wèn)題。最主要的效率問(wèn)題就是 父類構(gòu)造函數(shù)始終會(huì)被調(diào)用兩次 :一次在是創(chuàng)建子類原型時(shí)調(diào)用,另一次是在子類構(gòu)造函數(shù)中調(diào)用
四、原型式繼承
重點(diǎn): 用一個(gè)函數(shù)包裝一個(gè)對(duì)象,然后返回這個(gè)函數(shù)的調(diào)用,這個(gè)函數(shù)就變成了個(gè)可以隨意增添屬性的實(shí)例或?qū)ο蟆bject.create()就是這個(gè)原理。
//核心代碼
function object(o) {
function F() {}
F.prototype = o
return new F()
}
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"],
}
let anotherPerson = object(person)
anotherPerson.name = "Greg"
anotherPerson.friends.push("Rob")
let yetAnotherPerson = object(person)
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("Barbie")
console.log(person.friends) // "Shelby,Court,Van,Rob,Barbie"
復(fù)制代碼
特點(diǎn): 類似于復(fù)制一個(gè)對(duì)象,用函數(shù)來(lái)包裝。
缺點(diǎn):
- 所有實(shí)例都會(huì)繼承原型上的屬性。
- 無(wú)法實(shí)現(xiàn)復(fù)用。(新實(shí)例屬性都是后面添加的)
原型式繼承非常適合不需要單獨(dú)創(chuàng)建構(gòu)造函數(shù),但仍然需要在對(duì)象間共享信息的場(chǎng)合。但要記住,屬性中包含的引用值始終會(huì)在相關(guān)對(duì)象間共享,跟使用原型模式是一樣的
五、寄生式繼承
重點(diǎn): 就是給原型式繼承外面套了個(gè)殼子。
function object(o) {
function F() {}
F.prototype = o
return new F()
}
function createAnother(original) {
let clone = object(original) // 通過(guò)調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象
clone.sayHi = function () {
// 以某種方式增強(qiáng)這個(gè)對(duì)象
console.log("hi")
}
return clone // 返回這個(gè)對(duì)象
}
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"],
}
let anotherPerson = createAnother(person)
anotherPerson.sayHi() // "hi"
//寄生式繼承同樣適合主要關(guān)注對(duì)象,而不在乎類型和構(gòu)造函數(shù)的場(chǎng)景。object()函數(shù)不是寄生式繼承所必需的,任何返回新對(duì)象的函數(shù)都可以在這里使用。
// 注意 通過(guò)寄生式繼承給對(duì)象添加函數(shù)會(huì)導(dǎo)致函數(shù)難以重用,與構(gòu)造函數(shù)模式類似。
復(fù)制代碼
優(yōu)點(diǎn): 沒(méi)有創(chuàng)建自定義類型,因?yàn)橹皇翘琢藗€(gè)殼子返回對(duì)象(這個(gè)),這個(gè)函數(shù)順理成章就成了創(chuàng)建的新對(duì)象。
缺點(diǎn): 沒(méi)用到原型,無(wú)法復(fù)用。
六、寄生組合式繼承(常用)
重點(diǎn): 通過(guò)借用構(gòu)造函數(shù)繼承屬性 ,但使用混合式原型鏈繼承方法。基本思路是不通過(guò)調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是取得父類原型的一個(gè)副本。說(shuō)到底就是使用寄生式繼承來(lái)繼承父類原型,然后將返回的新對(duì)象賦值給子類原型。
寄生: 在函數(shù)內(nèi)返回對(duì)象然后調(diào)用
組合:
- 函數(shù)的原型等于另一個(gè)實(shí)例。
- 在函數(shù)中用 apply 或者 call 引入另一個(gè)構(gòu)造函數(shù),可傳參
function object(o) {
function F() {}
F.prototype = o
return new F()
}
/*function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 創(chuàng)建對(duì)象
prototype.constructor = subType; // 增強(qiáng)對(duì)象
subType.prototype = prototype; // 賦值對(duì)象
}*/
function SuperType(name) {
this.name = name
this.colors = ["red", "blue", "green"]
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
let prototype = object(superType.prototype) // 創(chuàng)建對(duì)象
subType.prototype = prototype // 賦值對(duì)象
prototype.constructor = subType // 修復(fù)實(shí)例
//inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
console.log(this.age)
}
復(fù)制代碼
優(yōu)先: 修復(fù)了組合繼承的問(wèn)題
缺點(diǎn): 實(shí)現(xiàn)麻煩
文章出自:??前端餐廳ReTech??,如有轉(zhuǎn)載本文請(qǐng)聯(lián)系前端餐廳ReTech今日頭條號(hào)。
github:??https://github.com/zuopf769??