面向對象-繼承
三個類
① 超類型
② 父類型
③ 子類型
④ 舉例:
類: 動物 狗類 哈士奇
對于狗類來說:動物是它的父類型,而哈士奇類是它的子類型
對于哈士奇類來說:狗類是它的父類型,動物類是它的超類型.
繼承:就是讓子類擁有父類的資源
原型鍊繼承
借用構造函數繼承
組合繼承 ????
原型式繼承
寄生式繼承
寄生式組合繼承
拷貝屬性繼承
減少代碼備援
友善統一操作
弊端:耦合性比較強
繼承-原型鍊繼承-原型鍊實戰
每個函數都能建構出一個對象, 這個對象内部有個屬性指向着這個函數的原型對象
原型對象本質也是一個對象,也是由另外一個構造函數構造出來, 也指向那個構造函數的原型對象
以上,形成一個鍊式的結構,就稱為是原型鍊
實戰:畫出數組的完整原型鍊
運作結果:
圖示:
繼承-原型鍊繼承-原型鍊檢索規則
對象.屬性的方法去通路屬性的時候,先查找有沒有對應的執行個體屬性,如果有那麼就直接使用
如果沒有,那麼就去該對象的原型對象上面去找,如果有那麼就直接使用
如果沒有,那麼就接着查找原型對象的原型對象,如果有,那麼就直接使用,
如果沒有,那麼就繼續上面的搜尋過程
直到搜尋到object.prototype為止,如果還是沒有找到就傳回undefined或者是報錯
注意:原型鍊搜尋的路徑越長,查詢屬性所花費的時間就越多
原則:就近原型
繼承-原型鍊繼承-0
未繼承效果:stu對象, 通路不到person裡面的任何内容
問題:子對象無法通路到父類的任何東西
繼承-原型鍊繼承-1
思路
① 查找規則:自己沒有, 就到原型對象上面查找…
② 是以:
Ⅰ 要不考慮給添加到自己身上:隻能自己有,以後的其他對象, 就沒了
Ⅱ 要不考慮給添加到原型身上
解決方案:修改原型指向比較友善
圖解
問題:無法通路到父類的對象屬性?
繼承-原型鍊繼承-2
思路:怎樣才能擁有父類的執行個體屬性和原型屬性?
構造父類的執行個體
解決方案:構造父類的執行個體,并設定為子類的原型對象
圖解:
問題:類型問題
繼承-原型鍊繼承-3
解決方案:修複constructor指針即可
問題:繼承過來的執行個體屬性, 如果是引用類型, 會被多個子類的執行個體共享
注意:到此為止,原型鍊繼承已經結束????
繼承-原型鍊繼承-4
思路:隻要在原型對象上,肯定都會被共享
解決方案
① 添加到對象自己身上
在建立過對象後添加:複用性差,備援度高, 穩定性差, 不采用
在構造函數内部添加
② 優化
可以直接調用父類構造函數, 但是需要修改this指向
③ 再優化
注意覆寫關系,如果産生重名, 應該, 子類覆寫父類。即把繼承的放在上面
概念 ????
“借用構造函數繼承”:在子構造函數内部, 調用父構造函數
問題:父類構造函數的參數無法修改
注意 ????
從此處開始,已經涵蓋了原型鍊繼承和借助構造函數繼承
稱之為**“組合繼承”= 原型鍊 + 借助構造函數**
繼承-原型鍊繼承-5
父類構造函數, 需要設定接收可變參數
子類構造函數在調用父類構造函數的時候, 傳遞參數即可
問題:父類屬性重複
執行個體上有一份,原型對象上有一份
繼承-原型鍊繼承-6
① 了解為什麼會有兩份?因為調用了兩次父類構造函數。1次, 在子類構造函數内部,2次, 建立子類構造函數原型對象時
② 分析:兩次調用的意義?
1次, 在子類構造函數内部:為了擷取父類的執行個體屬性
2次, 建立子類構造函數原型對象時:為了擷取父類的執行個體屬性,為了擷取執行個體的原型對象屬性
③ 結論::想辦法, 設定子類構造函數的原型對象時, 隻要父類構造函數的原型對象屬性
① 方案一:修改子類構造函數的原型對象指針, 為父類構造函數的原型對象
問題:共享原型對象、容易引發沖突、無法判定類型
不采用
② 方案二
能不能修改子類構造函數的原型對象指針, 為一個對象
這個對象, 隻能通路到父類原型對象的屬性/方法?
① 拷貝父類構造函數的原型對象
注意: 此處是指拷貝原型對象的内容, 不是整個位址過來
後面會講解如何拷貝, 到時自行實作這塊
② 借助父類構造函數的原型對象, 建立出來一個空對象執行個體
此處用到了寄生式繼承,是以最終, 到此為止, 變成了:寄生組合式繼承!!
這是業界程式猿公認的引用類型最理想的範式
① 原型式繼承
借助原型,然後基于已有的對象, 建立出新對象;同時不需要建立自定義類型
核心
系統實作:object.create
① 作用:建立對象,并且設定該對象的原型對象為傳遞過來的參數
相容es5,ie8不支援
② 簡單用法
建立了一個空對象obj2
把obj, 作為obj2的原型對象
③ 相容處理
② 寄生式繼承
在原型式基礎上增強這個對象,所謂增加, 就是指, 再次給這個對象增加一些屬性或者方法
面向對象-繼承-原型鍊繼承-總結
使用借助構造函數繼承的方式
在子構造函數中, 調用父構造函數
注意修改this指針
注意調用順序
寄生式繼承方式
面向對象-繼承-原型鍊繼承-練習-30m
繼承兩步走:
1. 借助構造函數繼承
2. 原型鍊繼承 和 寄生式繼承
3. 注意!!!person的原型對象一定要放在繼承的後面,不然會被繼承的内容覆寫。
執行個體屬性
name
age
原型方法
eat
run
父構造函數全部
job
jump
classname
study
面向對象-繼承-應用練習(擴充内置對象)
直接給對象動态添加屬性和方法
弊端: 如果操作很多個對象, 則無法共享;代碼比較備援
直接給array原型對象添加方法
弊端: 可能會産生覆寫的情況
提供一個新的構造函數,修改構造函數的原型指向為數組的原型對象,為了能夠擷取數組裡面的屬性和方法
問題: 依然會修改數組原型對象内容
優化: 原型對象就是一個對象,可以直接根據array建立一個對象, 給新建立的函數原型對象進行指派
補充
函數的prototype屬性
每個函數都有一個prototype屬性, 它預設指向一個object空對象(即稱為: 原型對象)
原型對象中有一個屬性constructor, 它指向函數對象
給原型對象添加屬性(一般都是方法)
函數的所有執行個體對象自動擁有原型中的屬性(方法)
每個函數function都有一個prototype,稱為顯式原型(屬性)
每個執行個體對象都有一個__proto__,稱為隐式原型(屬性)
對象的隐式原型的值為其對應構造函數的顯式原型的值
我們能直接操作顯式原型, 但不能直接操作隐式原型(es6之前)
内部語句: this.proto = fn.prototype
兩個方法是function的原型對象上面的方法
兩個函數的作用是一樣的,都可以用來借用方法實作
參數:
第一個參數是調用該方法的對象(函數内部的this綁定的對象)
第二個參數:call:參數清單;apply:數組
執行個體
對象1.方法.call(調用方法的真正的對象,參數1,參數2,參數3);
對象1.方法.apply(調用方法的真正的對象,[參數1,參數2,參數3…])
this – >總是指向一個對象
函數的調用方式
① 作為對象的方法來調用 this—>目前的對象
② 作為普通的函數調用 this—>window
③ 作為構造函數和new使用 this—>構造函數内部新建立的對象
④ 被call或者是apply調用(函數上下文調用) this—>第一個參數