天天看點

JavaScript:繼承詳解

面向對象-繼承

三個類

① 超類型

② 父類型

③ 子類型

④ 舉例:

類: 動物 狗類 哈士奇

對于狗類來說:動物是它的父類型,而哈士奇類是它的子類型

對于哈士奇類來說:狗類是它的父類型,動物類是它的超類型.

繼承:就是讓子類擁有父類的資源

原型鍊繼承

借用構造函數繼承

組合繼承 ????

原型式繼承

寄生式繼承

寄生式組合繼承

拷貝屬性繼承

減少代碼備援

友善統一操作

弊端:耦合性比較強

繼承-原型鍊繼承-原型鍊實戰

每個函數都能建構出一個對象, 這個對象内部有個屬性指向着這個函數的原型對象

原型對象本質也是一個對象,也是由另外一個構造函數構造出來, 也指向那個構造函數的原型對象

以上,形成一個鍊式的結構,就稱為是原型鍊

實戰:畫出數組的完整原型鍊

運作結果:

JavaScript:繼承詳解

圖示:

JavaScript:繼承詳解

繼承-原型鍊繼承-原型鍊檢索規則

對象.屬性的方法去通路屬性的時候,先查找有沒有對應的執行個體屬性,如果有那麼就直接使用

如果沒有,那麼就去該對象的原型對象上面去找,如果有那麼就直接使用

如果沒有,那麼就接着查找原型對象的原型對象,如果有,那麼就直接使用,

如果沒有,那麼就繼續上面的搜尋過程

直到搜尋到object.prototype為止,如果還是沒有找到就傳回undefined或者是報錯

注意:原型鍊搜尋的路徑越長,查詢屬性所花費的時間就越多

原則:就近原型

繼承-原型鍊繼承-0

未繼承效果:stu對象, 通路不到person裡面的任何内容

JavaScript:繼承詳解

問題:子對象無法通路到父類的任何東西

繼承-原型鍊繼承-1

思路

① 查找規則:自己沒有, 就到原型對象上面查找…

② 是以:

Ⅰ 要不考慮給添加到自己身上:隻能自己有,以後的其他對象, 就沒了

Ⅱ 要不考慮給添加到原型身上

解決方案:修改原型指向比較友善

圖解

JavaScript:繼承詳解

問題:無法通路到父類的對象屬性?

繼承-原型鍊繼承-2

思路:怎樣才能擁有父類的執行個體屬性和原型屬性?

構造父類的執行個體

解決方案:構造父類的執行個體,并設定為子類的原型對象

圖解:

JavaScript:繼承詳解

問題:類型問題

繼承-原型鍊繼承-3

解決方案:修複constructor指針即可

JavaScript:繼承詳解

問題:繼承過來的執行個體屬性, 如果是引用類型, 會被多個子類的執行個體共享

注意:到此為止,原型鍊繼承已經結束????

繼承-原型鍊繼承-4

思路:隻要在原型對象上,肯定都會被共享

解決方案

① 添加到對象自己身上

在建立過對象後添加:複用性差,備援度高, 穩定性差, 不采用

在構造函數内部添加

② 優化

可以直接調用父類構造函數, 但是需要修改this指向

③ 再優化

注意覆寫關系,如果産生重名, 應該, 子類覆寫父類。即把繼承的放在上面

JavaScript:繼承詳解

概念 ????

“借用構造函數繼承”:在子構造函數内部, 調用父構造函數

問題:父類構造函數的參數無法修改

注意 ????

從此處開始,已經涵蓋了原型鍊繼承和借助構造函數繼承

稱之為**“組合繼承”= 原型鍊 + 借助構造函數**

繼承-原型鍊繼承-5

父類構造函數, 需要設定接收可變參數

子類構造函數在調用父類構造函數的時候, 傳遞參數即可

問題:父類屬性重複

執行個體上有一份,原型對象上有一份

繼承-原型鍊繼承-6

① 了解為什麼會有兩份?因為調用了兩次父類構造函數。1次, 在子類構造函數内部,2次, 建立子類構造函數原型對象時

JavaScript:繼承詳解

② 分析:兩次調用的意義?

1次, 在子類構造函數内部:為了擷取父類的執行個體屬性

2次, 建立子類構造函數原型對象時:為了擷取父類的執行個體屬性,為了擷取執行個體的原型對象屬性

③ 結論::想辦法, 設定子類構造函數的原型對象時, 隻要父類構造函數的原型對象屬性

① 方案一:修改子類構造函數的原型對象指針, 為父類構造函數的原型對象

問題:共享原型對象、容易引發沖突、無法判定類型

不采用

② 方案二

能不能修改子類構造函數的原型對象指針, 為一個對象

這個對象, 隻能通路到父類原型對象的屬性/方法?

① 拷貝父類構造函數的原型對象

注意: 此處是指拷貝原型對象的内容, 不是整個位址過來

後面會講解如何拷貝, 到時自行實作這塊

② 借助父類構造函數的原型對象, 建立出來一個空對象執行個體

JavaScript:繼承詳解

此處用到了寄生式繼承,是以最終, 到此為止, 變成了:寄生組合式繼承!!

這是業界程式猿公認的引用類型最理想的範式

① 原型式繼承

借助原型,然後基于已有的對象, 建立出新對象;同時不需要建立自定義類型

核心

系統實作:object.create

① 作用:建立對象,并且設定該對象的原型對象為傳遞過來的參數

相容es5,ie8不支援

② 簡單用法

建立了一個空對象obj2

把obj, 作為obj2的原型對象

③ 相容處理

② 寄生式繼承

在原型式基礎上增強這個對象,所謂增加, 就是指, 再次給這個對象增加一些屬性或者方法

面向對象-繼承-原型鍊繼承-總結

使用借助構造函數繼承的方式

在子構造函數中, 調用父構造函數

注意修改this指針

注意調用順序

寄生式繼承方式

JavaScript:繼承詳解

面向對象-繼承-原型鍊繼承-練習-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…])

JavaScript:繼承詳解
JavaScript:繼承詳解

this – >總是指向一個對象

函數的調用方式

① 作為對象的方法來調用 this—>目前的對象

② 作為普通的函數調用 this—>window

③ 作為構造函數和new使用 this—>構造函數内部新建立的對象

④ 被call或者是apply調用(函數上下文調用) this—>第一個參數