原生JS繼承詳解
參考資料
不要講什麼設計模式 , 看得老子腦殼昏 , 智商欠費 , 還是簡單點 , 方案 , 繼承 , 就完事了
// 首先我們得有個父類
// 這個父類也很簡單
function Human(name) {
this.name = name || `肥宅` // 有參就取參 , 沒參就叫肥宅
}
Human.prototype.drink = function(drinks) {
console.log(this.name + `正在喝` + (drinks || `可樂`));
}
複製程式碼
OK, 我們現在有了一個父類, 先來看一個最簡單的繼承
最簡單的原型鏈繼承
直接把父類的例項作為子類的原型
// 先寫個空類
function Jianren(){}
Jianren.prototype = new Human()
// new個例項看看
var haidong = new Jianren();
console.log(haidong.name); //`肥宅`
console.log(haidong.drink()); //`haidong正在喝可樂`
複製程式碼
對於這個繼承, 概括為子類的原型鏈繫結父類的例項實現繼承
缺陷
- 不能實現多類繼承
- 為子類新增屬性和方法,必須要在new Animal()這樣的語句之後執行
- 無法控制子類能夠訪問父類屬性的許可權
- 建立子類的例項時也不能向父類建構函式傳參
構造繼承
複製父類屬性
function Jianren(){
Human.call(this);// 把父類寫進子類建構函式內部, 呼叫call把子類的this傳進去改變this的指向做到複製父類屬性
this.age = 22
}
複製程式碼
- 構造繼承其實就是複製了父類屬性, new Jianren()出來的例項的construct和prototype都與Human無關, 指向Jianren()
- 可以不管new 父類前後順序來新增子類的屬性
- 建立子類例項時, 也可以向父類傳遞引數,
- 可以call多個父類實現多類繼承
- 只能繼承父類的例項屬性和方法, 不能繼承/訪問到父類原型上的東西
- 難以複用, 影響效能
例項繼承
在子類內部新增一個屬性, 該屬性為父類的例項
function Jianren(name){
var ren = new Human();// 注意這裡是不是this
ren.name = name || `haidong`;
return ren;
}
複製程式碼
- 不會限制呼叫的方式, new 子類() 或者 子類()執行, 都返回ren
- new Jianren()的例項實際上是new Human(), constructor也是指向Human()
- 不支援多類繼承
拷貝繼承
function Jianren(name){
var ren = new Human()
for(var r in ren) {
Cat.prototype[r] = ren[r] // 該處r是key, 應該用Object[key]的形式賦值和訪問
}
Jianren.prototype.name = name || `haidong`
}
複製程式碼
- 支援多繼承
- 效率低, 拷貝屬性會佔用記憶體
- 不可列舉父類
推薦的兩種繼承
組合繼承和寄生組合繼承
上面的繼承主要是幫助理解, 或有時暴力快速解決小問題時使用 .
推薦一, 組合繼承
子類的原型指向父類例項,該父類例項又可向上原型查詢訪問,修正後constructor指向子類自身
function Jianren(name) {
Human.call(this) //第一步拷貝屬性
this.name = name || `haidong`
}
Jianren.prototype = new Human() //第二步, 原型指向父類例項
Jianren.prototype.constructor = Jianren //第三步, 修正constructor指向
複製程式碼
- 可以繼承例項的屬性/方法, 也可以繼承原型的屬性方法,
- 是子類的例項, 也相當於是父類的例項
- 子類的原型指向父類例項,該父類例項又可向上原型查詢訪問,修正後constructor指向子類自身
- 可傳參,//call()可以傳參,具體用法,自行百度谷歌
- 呼叫兩次父類建構函式, 佔記憶體, 第一步拷貝過一次, 第二步導致以後的Jianren的例項裡和Jianren原型上重複的屬性
推薦二, 寄生組合繼承
// 有點複雜
function Jianren(name) {
Human.call(this) //第一步拷貝屬性
this.name = name || `haidong`
}
(function() { //來個匿名立即執行函式
function J() {}// 建立一箇中間類, 空, 什麼都沒有
J.prototype = Human.prototype //第二步中間類原型指向父類原型
Jianren.prototype = new J() //子類原型賦值為中間類的例項,往上查詢,指向了父類的原型
})()
Jianren.prototype.constructor = Jianren //修正子類constructor指向
複製程式碼
與上面的組合繼承相比, 第一步拷貝 和 第二步中間類(空類)不會重複有Human的屬性, 略微複雜, 可以忽略不計, 完美方式