原型鏈繼承
let Parent = function() {
this.name = [‘parent']
}
Parent.prototype.sayName = function() {
console.log(this.name)
}
let Child = function(){}
Child.prototype = new Parent() //原型式繼承
let child1 = new Child()
child1.sayName() //[‘parent’]
child1.name.push(‘child’)
let child2 = new Child()
child2.sayName() //[‘parent’, ‘child']
問題:
- 所以實例共享引用類型的屬性,一改都改;
- 且創建 child 實例無法向 Parent 傳參
組合式繼承
這是 JavaScript 中最常用的繼承模式。
let Parent = function(name){
this.name = name
this.staff = [‘a']
}
Parent.prototype.sayName = function(){
console.log(this.name)
}
Parent.prototype.showStaff = function(){
console.log(this.staff)
}
let Child = function(name){
Parent.call(this, name) //借用 Parent 構造函數,實現向其傳參 (第二次)
}
Child.prototype = new Parent() // 繼承 Parent 原型上的方法 (第一次)
let child1 = new Child(‘child1’)
let child2 = new Child(‘child2’)
child1.sayName() //child1
child2.sayName() //child2
child1.staff.push(‘child1’)
child1.showStaff() //[‘a’, ‘child1’]
child2.showStaff() //[‘a’]
問題:
兩次調用 Parent
構造函數,分別讓 Child.prototype
、child
實例兩個對象都有了 Parent
中的 name
、staff
屬性。
寄生組合繼承
關鍵:
對於第一次調用,讓 Child.prototype
有了 Parent
的屬性,這是要避免的。
方法:
- 創建一個空的函數中轉一下;
- 讓
Child
的原型Child.prototype
= 空函數的實例; - 讓空函數的原型
F.prototype = Parent.prototype
。
代碼描述
let F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
由此,child
通過 __proto__
指向Child.prototype
即 f
,又通過 __proto__
指向 F.prototype
即 Parent.prototype
事實上,上面的思路就是 Object.create()
的實現
用代碼描述:
function create (obj) {
let F = function(){}
F.prototype = obj
return new F()
}
由於 Child.prototype = new F()
,需要重新做 constructor
等指向,最後得
function inheritPrototype (subType, superType) {
let prototype = Object.create(superType.prototype)
subType.prototype = prototype
prototype.constructor = subType
寄生組合繼承代碼
let Parent = function(name){
this.name = name
this.staff = [1]
}
Parent.prototype.sayName = function(){
console.log(this.name)
}
Parent.prototype.showStaff = function(){
console.log(this.staff)
}
let Child = function(name){
Parent.call(this, name)
}
//Child.prototype = new Parent()
function inheritPrototype (subType, superType) {
let prototype = Object.create(superType.prototype)
subType.prototype = prototype
prototype.constructor = subType
}
inheritPrototype(Child, Parent)
let child1 = new Child(‘child1’)
child1.sayName()
let child2 = new Child(‘child2’)
child2.sayName()
child1.staff.push(2)
child1.showStaff()
child2.showStaff()
好處
《JavaScript高級程序設計》表示:
這種方式的高效率體現它只調用了一次 Parent 構造函數,並且因此避免了在 Parent.prototype
上面創建不必要的、多餘的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用 instanceof 和
isPrototypeOf。開發人員普遍認爲寄生組合式繼承是引用類型最理想的繼承範式。