//8.繼承
構造函數方式繼承
構造函數方式繼承的缺點:構造函數的方法會在實例化的時候多次執行,沒有必要。
//8.1構造函數方式繼承
function SuperType1(){
this.property = true
this.arr = [1,2,3]
this.fuc = function () {
console.log('just test')
}
}
function SubType1() {
SuperType1.call(this)
this.subproperty = false
}
var obj1 = new SubType1()
var obj2 = new SubType1()
obj1.arr.push(4)
console.log(obj1.arr)//[1, 2, 3, 4] 引用類型沒有互相污染,但是fuc方法每次實例話都會新生成一次,沒必要。
console.log(obj2.arr)//[1, 2, 3] 引用類型沒有互相污染,但是fuc方法每次實例話都會新生成一次,沒必要。
原型方式繼承
原型方式繼承缺點:如果父級構造函數內有引用類型,如果此時有兩個實例對象,其中一個實例對象改變了引用類型的值,則另一個實例對象的引用類型值也會相應改變。這不是我們期望的。
//8.2原型方式繼承 (為什么原型繼承,因為構造函數方式繼承的話,每次new一個實例,相同的方法都會重新生成一次,沒有必要)
function SuperType2(){
this.property = true
this.arr = [1,2,3]
}
SuperType2.prototype.fuc = function(){//將構造函數繼承里的fuc提出來到原型繼承里
console.log('just test')
}
function SubType2(){
this.subproperty = false
}
SubType2.prototype = new SuperType2()//將SubType2的prototype設置為需要繼承的構造函數的實例
var obj3 = new SubType2()
var obj4 = new SubType2()
obj3.arr.push(4)
console.log(obj3.arr)//[1, 2, 3, 4] 原型鏈繼承方式,會讓引用類型的值發生改變互相污染
console.log(obj4.arr)//[1, 2, 3, 4] 原型鏈繼承方式,會讓引用類型的值發生改變互相污染
obj4.property = false
console.log(obj3.property)//true 基本類型值不會污染
console.log(obj4.property)//false 基本類型值不會污染
組合方式繼承(構造函數+原型鏈)
由于構造函數方式繼承會導致函數內方法多次實例化(沒有必要),原型鏈繼承又有可能導致引用類型值的改變。所以現在考慮將兩者相結合。
//8.3 構造函數+原型鏈組合方式繼承(思路:把屬性放在構造函數里,把方法提出到prototype里,子構造函數的protoype指向父構造函數的實例)
function SuperType3(){
this.property = true
this.arr = [1,2,3]
}
SuperType3.prototype.fuc = function(){//將構造函數繼承里的fuc提出來到原型繼承里
console.log('just test')
}
function SubType3(){
//繼承屬性
SuperType3.call(this)//等價于下方注釋部分,但由于是繼承,所以要這樣寫,因為大多數時候你并不知道父構造函數里有什么
// this.property = true
// this.arr = [1,2,3]
this.subproperty = false
}
SubType3.prototype = new SuperType3()
var obj5 = new SubType3()
var obj6 = new SubType3()
obj5.arr.push(4)
console.log(obj5.arr)//[1, 2, 3, 4] 引用類型放在構造函數內,沒有對其他實例造成污染
console.log(obj6.arr)//[1, 2, 3] 引用類型放在構造函數內,沒有對其他實例造成污染
組合方式優化1
上述組合方式繼承還有問題,就是:
(1)SubType3.prototype = new SuperType3()時會調用SuperType3;
(2)SubType3內部執行SuperType3.call(this)時又會調用一次SuperType3。
//8.4 組合方式優化1
function SuperType4(){
this.property = true
this.arr = [1,2,3]
}
SuperType4.prototype.fuc = function(){//將構造函數繼承里的fuc提出來到原型繼承里
console.log('just test')
}
function SubType4(){
//繼承屬性
SuperType4.call(this)//等價于下放注釋部分,但由于是繼承,所以要這樣寫,因為大多數時候你并不知道父構造函數里有什么
// this.property = true
// this.arr = [1,2,3]
this.subproperty = false
}
SubType4.prototype = SuperType4.prototype
var obj7 = new SubType4()
var obj8 = new SubType4()
obj7.arr.push(4)
console.log(obj7.constructor)//? SuperType4()
組合方式優化2
上述優化1依然有問題,雖然SuperType4只調用了一次,但是
...
SubType4.prototype = SuperType4.prototype
...
這里又會導致SubType4實例化對象的constructor實際是指向SuperType4的。
所以,我們將此處修改為:
...
SubType4.prototype = SuperType4.prototype
SubType4.prototype.constructor = SubType4
...
//加Object.create()前
這樣SubType4的原型對象的contructor屬性就指向SubType4了。但這樣就夠了嗎?我們發現,此時SuperType4.prototype.constructor也指向了SubType4,這不是我們期望的。
所以還需要做以下這步:
...
SubType4.prototype = Object.create(SuperType4.prototype)
SubType4.prototype.constructor = SubType4
...
//加Object.create()后
對比一下前后兩者區別:
//console.log(SuperType4.prototype)
{fuc: ?, constructor: ?}
fuc:? ()
constructor:? SuperType4() //若直接SubType4.prototype = SuperType4.prototype,SuperType4將會被替換成SubType4
__proto__:Object
//console.log(Object.create(SuperType4.prototype))
SuperType4 {}
__proto__: //通過SubType4.prototype = Object.create(SuperType4.prototype)創建一個新的對象,則會在__proto__平級處會增加一個constructor: SubType4。下方的SuperType4并不會被替換。
fuc:? ()
constructor:? SuperType4()
__proto__:Object
上述代碼中,Object.create(SuperType4.prototype)基本等價于:
function object(o){//返回一個函數的實例,該實例的原型對象等于傳入的參數
function F(){};
F.prototype = o;
return new F();
}
object(SuperType4.prototype)
也就是說,Object.create()方法會創建一個新的對象,對象的prototype即為傳入的參數。
最終,我們得到了最優的繼承方式:
function SuperType4(){
this.property = true
this.arr = [1,2,3]
}
SuperType4.prototype.fuc = function(){//將構造函數繼承里的fuc提出來到原型繼承里
console.log('just test')
}
function SubType4(){
//繼承屬性
SuperType4.call(this)//等價于下放注釋部分,但由于是繼承,所以要這樣寫,因為大多數時候你并不知道父構造函數里有什么
// this.property = true
// this.arr = [1,2,3]
this.subproperty = false
}
SubType4.prototype = Object.create(SuperType4.prototype)
SubType4.prototype.constructor = SubType4