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