好程式設計師分享JavaScript六種繼承方式詳解
好程式設計師 分享 JavaScript 六種繼承方式詳解 , 繼承是物件導向程式設計中又一非常重要的概念, JavaScript 支援實現繼承,不支援介面繼承,實現繼承主要依靠原型鏈來實現的
原型鏈
首先得要明白什麼是原型鏈,在一篇文章看懂 proto 和 prototype 的關係及區別中講得非常詳細
原型鏈繼承基本思想就是讓一個原型物件指向另一個型別的例項
function SuperType() {
this.property = true
}
SuperType.prototype.getSuperValue = function() {
return this.property
}
function SubType() {
this.subproperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function() {
return this.subproperty
}
var instance = new SubType() console.log(instance.getSuperValue()) // true
程式碼定義了兩個型別 SuperType 和 SubType ,每個型別分別有一個屬性和一個方法, SubType 繼承了 SuperType ,而繼承是透過建立 SuperType 的例項,並將該例項賦給 SubType.prototype 實現的
實現的本質是重寫原型物件 , 代之以一個新型別的例項,那麼存在 SuperType 的例項中的所有屬性和方法,現在也存在於 SubType.prototype 中了
我們知道,在建立一個例項的時候,例項物件中會有一個內部指標指向建立它的原型,進行關聯起來,在這裡程式碼 SubType.prototype = new SuperType() ,也會在 SubType.prototype 建立一個內部指標,將 SubType.prototype 與 SuperType 關聯起來
所以 instance 指向 SubType 的原型, SubType 的原型又指向 SuperType 的原型,繼而在 instance 在呼叫 getSuperValue() 方法的時候,會順著這條鏈一直往上找
新增方法
在給 SubType 原型新增方法的時候,如果,父類上也有同樣的名字, SubType 將會覆蓋這個方法,達到重新的目的。 但是這個方法依然存在於父類中
記住不能以字面量的形式新增,因為,上面說過透過例項繼承本質上就是重寫,再使用字面量形式,又是一次重寫了,但這次重寫沒有跟父類有任何關聯,所以就會導致原型鏈截斷
function SuperType() {
this.property = true
}
SuperType.prototype.getSuperValue = function() {
return this.property
}
function SubType() {
this.subproperty = false
}
SubType.prototype = new SuperType()
SubType.prototype = {
getSubValue: function() {
return this.subproperty
}
}
var instance = new SubType() console.log(instance.getSuperValue()) // error
問題
單純的使用原型鏈繼承,主要問題來自包含引用型別值的原型。
function SuperType() {
this.colors = ['red', 'blue', 'green']
}
function SubType() {}
SubType.prototype = new SuperType()
var instance1 = new SubType() var instance2 = new SubType()
instance1.colors.push('black') console.log(instance1.colors) // ["red", "blue", "green", "black"]
console.log(instance2.colors) // ["red", "blue", "green", "black"]
在 SuperType 建構函式定義了一個 colors 屬性,當 SubType 透過原型鏈繼承後,這個屬性就會出現 SubType.prototype 中,就跟專門建立了 SubType.prototype.colors 一樣,所以會導致 SubType 的所有例項都會共享這個屬性,所以 instance1 修改 colors 這個引用型別值,也會反映到 instance2 中
借用建構函式
此方法為了解決原型中包含引用型別值所帶來的問題
這種方法的思想就是在子類建構函式的內部呼叫父類建構函式,可以藉助 apply() 和 call() 方法來改變物件的執行上下文
function SuperType() {
this.colors = ['red', 'blue', 'green']
}
function SubType() {
// 繼承 SuperType
SuperType.call(this)
}
var instance1 = new SubType() var instance2 = new SubType()
instance1.colors.push('black') console.log(instance1.colors) // ["red", "blue", "green", "black"]
console.log(instance2.colors) // ["red", "blue", "green"]
在新建 SubType 例項是呼叫了 SuperType 建構函式,這樣以來,就會在新 SubType 物件上執行 SuperType 函式中定義的所有物件初始化程式碼
結果, SubType 的每個例項就會具有自己的 colors 屬性的副本了
傳遞引數
藉助建構函式還有一個優勢就是可以傳遞引數
function SuperType(name) {
this.name = name
}
function SubType() {
// 繼承 SuperType
SuperType.call(this, 'Jiang')
this.job = 'student'
}
var instance = new SubType() console.log(instance.name) // Jiang
console.log(instance.job) // student
問題
如果僅僅藉助建構函式,方法都在建構函式中定義,因此函式無法達到複用
組合繼承 ( 原型鏈 + 建構函式 )
組合繼承是將原型鏈繼承和建構函式結合起來,從而發揮二者之長的一種模式
思路就是使用原型鏈實現對原型屬性和方法的繼承,而透過借用建構函式來實現對例項屬性的繼承
這樣,既透過在原型上定義方法實現了函式複用,又能夠保證每個例項都有它自己的屬性
function SuperType(name) {
this.name = name this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function() {
console.log(this.name)
}
function SubType(name, job) {
// 繼承屬性
SuperType.call(this, name)
this.job = job
}
// 繼承方法
SubType.prototype = new SuperType() SubType.prototype.constructor = SuperType SubType.prototype.sayJob = function() {
console.log(this.job)
}
var instance1 = new SubType('Jiang', 'student') instance1.colors.push('black') console.log(instance1.colors) //["red", "blue", "green", "black"]
instance1.sayName() // 'Jiang'
instance1.sayJob() // 'student'
var instance2 = new SubType('J', 'doctor') console.log(instance2.colors) // //["red", "blue", "green"]
instance2.sayName() // 'J'
instance2.sayJob() // 'doctor'
這種模式避免了原型鏈和建構函式繼承的缺陷,融合了他們的優點,是最常用的一種繼承模式
原型式繼承
藉助原型可以基於已有的物件建立新物件,同時還不必因此建立自定義型別
function object(o) {
function F() {}
F.prototype = o
return new F()
}
在 object 函式內部,先建立一個臨時性的建構函式,然後將傳入的物件作為這個建構函式的原型,最後返回這個臨時型別的一個新例項
本質上來說, object 對傳入其中的物件執行了一次淺複製
var person = {
name: 'Jiang',
friends: ['Shelby', 'Court']
}
var anotherPerson = object(person) console.log(anotherPerson.friends) // ['Shelby', 'Court']
這種模式要去你必須有一個物件作為另一個物件的基礎
在這個例子中, person 作為另一個物件的基礎,把 person 傳入 object 中,該函式就會返回一個新的物件
這個新物件將 person 作為原型,所以它的原型中就包含一個基本型別和一個引用型別
所以意味著如果還有另外一個物件關聯了 person , anotherPerson 修改陣列 friends 的時候,也會體現在這個物件中
Object.create() 方法
ES5 透過 Object.create() 方法規範了原型式繼承,可以接受兩個引數,一個是用作新物件原型的物件和一個可選的為新物件定義額外屬性的物件,行為相同,基本用法和上面的 object 一樣,除了 object 不能接受第二個引數以外
var person = {
name: 'Jiang',
friends: ['Shelby', 'Court']
}
var anotherPerson = Object.create(person) console.log(anotherPerson.friends)
寄生式繼承
寄生式繼承的思路與寄生建構函式和工廠模式類似,即建立一個僅用於封裝繼承過程的函式
function createAnother(o) {
var clone = Object.create(o) // 建立一個新物件
clone.sayHi = function() { // 新增方法
console.log('hi')
}
return clone // 返回這個物件
}
var person = {
name: 'Jiang'
}
var anotherPeson = createAnother(person) anotherPeson.sayHi()
基於 person 返回了一個新物件 anotherPeson ,新物件不僅擁有了 person 的屬性和方法,還有自己的 sayHi 方法
在主要考慮物件而不是自定義型別和建構函式的情況下,這是一個有用的模式
寄生組合式繼承
在前面說的組合模式 ( 原型鏈 + 建構函式 ) 中,繼承的時候需要呼叫兩次父類建構函式
父類
function SuperType(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
第一次在子類建構函式中
function SubType(name, job) {
// 繼承屬性
SuperType.call(this, name)
this.job = job
}
第二次將子類的原型指向父類的例項
// 繼承方法
SubType.prototype = new SuperType()
當使用 var instance = new SubType() 的時候,會產生兩組 name 和 color 屬性,一組在 SubType 例項上,一組在 SubType 原型上,只不過例項上的遮蔽了原型上的
使用寄生式組合模式,可以規避這個問題
這種模式透過借用建構函式來繼承屬性,透過原型鏈的混成形式來繼承方法
基本思路:不必為了指定子型別的原型而呼叫父類的建構函式,我們需要的無非就是父類原型的一個副本
本質上就是使用寄生式繼承來繼承父類的原型,在將結果指定給子型別的原型
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
該函式實現了寄生組合繼承的最簡單形式
這個函式接受兩個引數,一個子類,一個父類
第一步建立父類原型的副本,第二步將建立的副本新增 constructor 屬性,第三部將子類的原型指向這個副本
function SuperType(name) {
this.name = name this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function() {
console.log(this.name)
}
function SubType(name, job) {
// 繼承屬性
SuperType.call(this, name)
this.job = job
}
// 繼承
inheritPrototype(SubType, SuperType)
var instance = new SubType('Jiang', 'student') instance.sayName()
> 補充:直接使用 Object.create 來實現,其實就是將上面封裝的函式拆開,這樣演示可以更容易理解
function SuperType(name) {
this.name = name this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function() {
console.log(this.name)
}
function SubType(name, job) {
// 繼承屬性
SuperType.call(this, name)
this.job = job
}
// 繼承
SubType.prototype = Object.create(SuperType.prototype)
// 修復 constructor
SubType.prototype.constructor = SubType
var instance = new SubType('Jiang', 'student') instance.sayName()
ES6 新增了一個方法, Object.setPrototypeOf ,可以直接建立關聯,而且不用手動新增 constructor 屬性
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913892/viewspace-2640961/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JavaScript 六種繼承方式JavaScript繼承
- JavaScript 中的六種繼承方式JavaScript繼承
- JavaScript 繼承方式詳解JavaScript繼承
- 好程式設計師分享JavaScript建立物件的方式!程式設計師JavaScript物件
- JavaScript中的六種繼承JavaScript繼承
- 好程式設計師web前端培訓分享JavaScript學習筆記閉包與繼承程式設計師Web前端JavaScript筆記繼承
- JavaScript的幾種繼承方式JavaScript繼承
- 好程式設計師web前端教程之JS繼承實現方式解析程式設計師Web前端JS繼承
- 好程式設計師Java培訓之泛型繼承原理與用法詳解程式設計師Java泛型繼承
- JS 繼承的 六 種實現方式JS繼承
- 好程式設計師分享JavaScript名稱空間模式例項詳解程式設計師JavaScript模式
- JavaScript繼承詳解(二)JavaScript繼承
- 好程式設計師分享placeholder屬性詳解程式設計師
- 好程式設計師前端教程css中可被繼承和不可被繼承的屬性程式設計師前端CSS繼承
- 好程式設計師Java培訓分享For迴圈詳解程式設計師Java
- 好程式設計師大資料教程Scala系列之繼承程式設計師大資料繼承
- 好程式設計師分享JavaScript事件委託代理和函式封裝詳解程式設計師JavaScript事件函式封裝
- javascript繼承的方式JavaScript繼承
- 三種繼承方式繼承
- 6種JavaScript繼承方式及優缺點JavaScript繼承
- javascript幾種繼承方式;不看就out啦JavaScript繼承
- Javascript中的幾種繼承方式比較JavaScript繼承
- 三種繼承的方法:public 繼承/private繼承/protected繼承詳解及區別繼承
- 好程式設計師分享Css詳解bem書寫規範程式設計師CSS
- 好程式設計師技術分析JavaScript閉包特性詳解程式設計師JavaScript
- Javascript 五十問——實現的繼承多種方式JavaScript繼承
- JavaScript繼承的多種方式和優缺點JavaScript繼承
- JavaScript 各種繼承方式優缺點對比JavaScript繼承
- javascript實現的繼承的幾種常用方式JavaScript繼承
- 好程式設計師web前端教程分享JavaScript面試題程式設計師Web前端JavaScript面試題
- 好程式設計師分享JavaScript之-文件物件模型(DOM)程式設計師JavaScript物件模型
- 好程式設計師Python培訓分享For迴圈用法詳解程式設計師Python
- 好程式設計師web前端教程之詳解JavaScript嚴格模式程式設計師Web前端JavaScript模式
- 好程式設計師web前端培訓分享詳解JavaScript學習筆記建構函式程式設計師Web前端JavaScript筆記函式
- Javascript 中實現物件原型繼承的三種方式JavaScript物件原型繼承
- 好程式設計師JavaScript教程分享JavaScript中變數和作用域程式設計師JavaScript變數
- JavaScript實現繼承的方式JavaScript繼承
- Javascript之繼承(原型鏈方式)JavaScript繼承原型