本篇部落格接著繼承的這6種方式!(上),繼續介紹你想知道的後三種繼承方式,尤其是最理想的寄生組合式繼承。
4. 原型式繼承
4.1 介紹
原型式繼承的基本思想:
- 1個基礎物件
- 1個新物件,把基礎物件作為原型物件
- 新物件建立例項
//基礎物件
var person = {
name: "一燈",
arr: [1,2,3]
}
//Object.create()建立新物件,傳入基礎物件
var son1 = Object.create(person)
son1.name = "AAA"
son1.arr.push(4)
console.log(son1.name) //AAA
var son2 = Object.create(person)
son2.name = "BBB"
console.log(son2.name) //BBB
console.log(son2.arr) //1,2,3,4,引用型別問題依然存在
複製程式碼
當然你也可以使用Object.create()的第二個引數傳新增物件屬性
var person = {
name: "一燈",
arr: [1,2,3]
}
var son1 = Object.create(person, {
name: {
value: "AAA"
}
})
son1.arr.push(4)
console.log(son1.name) //AAA
var son2 = Object.create(person, {
name: {
value: "BBB"
}
})
console.log(son2.name) //BBB
console.log(son2.arr) //1,2,3,4
複製程式碼
4.2 優劣分析
-
原型式繼承解決了原型鏈無法傳參的問題,並且無需使用建構函式(避免了建構函式的問題)。因此在沒必要使用建構函式時可以採用這種方法。
-
引用型別問題依舊存在
5. 寄生式繼承
5.1 介紹
寄生式繼承可以理解為是原型式繼承的增強。在原型式繼承中我們建立了一個新物件,寄生式繼承便是在新物件中新增方法,以增強物件。
var person = {
name: "一燈",
arr: [1,2,3]
}
//增強物件
function increase(obj, prop) {
var object = Object.create(obj,prop)
object.getName = function() {
console.log(this.name)
}
return object
}
var son1 = increase(person, {
name: {
value: "AAA"
}
})
son1.arr.push(4)
console.log(son1.name) //AAA
son1.getName() //AAA
var son2 = increase(person, {
name: {
value: "BBB"
}
})
console.log(son2.name) //BBB
console.log(son2.arr) //1,2,3,4
son2.getName() //BBB
複製程式碼
5.2 缺陷
寄生式繼承類似於建構函式,每個例項物件都有一個副本——破壞了複用性。
6. 寄生組合式繼承——大招來了
6.1 介紹
在繼承的這6種方式!(上)中講到的組合式繼承比較常用的一種方式,然而這種方式還是存在一個問題——父級建構函式呼叫了兩次(可以到上篇博文檢視程式碼)。一次在建立子級原型物件,另一次在子級建構函式內部。
對於精益求精的碼農,當然不能容忍,因此誕生了寄生組合式繼承,其核心思想是:
- 組合式:子級的prototype繼承父級的prototype——通過new Father()
- 寄生組合式:子級的prototype繼承父級的prototype——通過賦值
function inheritPrototype(Son, Father) {
//建立一個Father.prototype的副本
var prototype = Object.create(Father.prototype)
/*下面這句程式碼的很多資料的描述感覺不太清晰,我的理解:
*1、Father.prototype的作用是賦值給Son.prototype
*2、如果沒有下面這條語句:Son.prototype.constructor == Father建構函式
*3、因此需要更改Son.prototype.constructor,模擬new Father()的過程
*/
prototype.constructor = Son
//把Father.prototype賦值給 Son.prototype
Son.prototype = prototype
}
function Father(name) {
this.name = name
this.arr = [1,2,3]
}
Father.prototype.getName = function() {
console.log(this.name)
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
inheritPrototype(Son, Father)
Son.prototype.getAge = function() {
console.log(this.age)
}
var son1 = new Son("AAA", 23)
son1.getName() //AAA
son1.getAge() //23
son1.arr.push(4)
console.log(son1.arr) //1,2,3,4
var son2 = new Son("BBB", 24)
son2.getName() //BBB
son2.getAge() //24
console.log(son2.arr) //1,2,3
複製程式碼
6.2 優劣分析
-
寄生組合式繼承對於引用型別的繼承來說是最理想的繼承方式:避免了應用型別問題,並且只呼叫一次父級建構函式。
-
要說其缺點就是比其他方式更為複雜一些
總結
- JS共有6種方式實現繼承:
- 原型鏈:最原始的繼承方式(引用型別值相互影響、無法向父級建構函式傳參)
- 借用建構函式:解決原型鏈的問題,但破壞了複用性
- 組合式:原型鏈+借用建構函式(取長避短),但呼叫了兩次父級建構函式
- 原生式:解決原型鏈傳參問題,並且無需使用建構函式,但也存在引用型別問題
- 寄生式:原生式的增強
- 寄生組合式:寄生式+組合式,解決了各種問題,只是程式碼稍微複雜
- 理解原型/原型鏈對理解繼承幫助甚大,強烈建議先弄得JS原型——一張圖徹底KO原型鏈(prototype,__proto__)
更多優質文章將持續更新,來關注一波……