階段一
首先創(chuàng)建一個父類的函數方便繼承
function Animal(aniamlType,age){
this.aniamlType = aniamlType
this.age = age
}
然后再創(chuàng)建一個子類用來繼承
function Dog(aniamlType, age, name) {
Animal.call(this, aniamlType, age)
this.name = name
}
只需將this傳進去執(zhí)行一遍Animal函數栅盲,就能得到所有的屬性了湘今。
但是父類prototype的方法并不會被繼承下來!
function Animal(aniamlType, age) {
this.aniamlType = aniamlType
this.age = age
}
Animal.prototype.eat = function () {
console.log('eat')
}
function Dog(aniamlType, age, name) {
Animal.call(this, aniamlType, age)
this.name = name
}
var dog1 = new Dog('dog', '18', 'wb')
dog1.eat()//Uncaught TypeError: dog1.eat is not a function
階段二
由于父類的方法沒辦法繼承剪菱,所以這時候我們就考慮從子類的prototype上來選擇繼承摩瞎。這樣就能讓實例proto指向子類的prototype的時候能夠得到父類的方法和屬性了。
這時候我們將代碼改一改
function Animal(aniamlType, age) {
this.play = ['eat','bite','shout']
}
Animal.prototype.eat = function () {
console.log('eat')
}
function Dog(aniamlType, age, name) {
this.aniamlType = aniamlType
this.age = age
this.name = name
}
Dog.prototype = new Animal()
var dog1 = new Dog('dog', '18', 'wb')
dog1.eat() //eat
可以看到這樣就能讓實例得到父類的方法了孝常。
但是當我再創(chuàng)建一個實例的時候旗们,然后修改父類的時候,由于兩個實例的proto是子類的prototype构灸,而子類的prototype引用同一個地址上渴,即父類的實例(new Animal()),所以修改一個實例里面來自父類的對象的時候喜颁,另一個實例的那個對象也會跟著發(fā)生改變
var dog1 = new Dog('dog', '18', 'wb')
var dog2 = new Dog('dog', '20', 'wbd')
dog1.play.push('owwwww')
console.log(dog1.play)//["eat", "bite", "shout", "owwwww"]
console.log(dog2.play)//["eat", "bite", "shout", "owwwww"]
這種方法雖然可以繼承父類的方法稠氮,但是改變父類對象的時候,其他實例的這個對象也會跟著改變半开。
階段三
上述兩個階段都有其可取之處隔披,也有缺點。所以我們可以想到寂拆,應該能兩者一起混合使用奢米。于是就有了第三種方案了抓韩。
function Animal(aniamlType, age) {
this.aniamlType = aniamlType
this.age = age
this.play = ['eat', 'bite', 'shout']
}
Animal.prototype.eat = function () {
console.log('eat')
}
function Dog(aniamlType, age, name) {
Animal.call(this, aniamlType, age, name)//使用階段一來繼承屬性
this.name = name
}
Dog.prototype = new Animal()//使用階段二來繼承方法
var dog1 = new Dog('dog', '18', 'wb')
var dog2 = new Dog('dog', '20', 'wbd')
dog1.play.push('owwwww')
console.log(dog1.play)//["eat", "bite", "shout", "owwwww"]
console.log(dog2.play)//["eat", "bite", "shout"]
這時候更改一個實例的時候就不會出現階段三的問題了。
但是子類new鬓长,實例也要new一下谒拴,會增加性能損耗
于是想到直接等于讓子類的prototype = 父類的prototype好了
Dog.prototype = Animal.prototype
但是這時候問題又來了,子類在prototype上添加的方法會直接影響到父類的prototype涉波,不方便別的子類繼承該父類英上。而且子類的constructor會發(fā)生改變。
Dog.prototype.cat = function () {
console.log('cat')
}
console.log(Animal.prototype.cat) // function () { console('cat') }
console.log(dog1.constructor)//function Animal ....
在上面會發(fā)現創(chuàng)建實例的竟然是爺爺啤覆,而不是爸爸苍日。這就亂套了。
所以這時候就需要將子類的prototype復制父類的prototype而不是直接引用了城侧。這時候不僅父類沒有了方法易遣,而且還可以直接修改子類的constructor了
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.cat = function () {
console.log('cat')
}
Dog.prototype.constructor = Dog
console.log(Animal.prototype.cat) //undefined
console.log(dog1.constructor) //function Dog
這時候才是完美的面向對象的繼承彼妻。
總結:
1.在子類內部繼承屬性
2.在子類的prototype上復制父類的prototype
3.更改子類prototype.constructor嫌佑,讓其指向自己。
更簡單方便的方法侨歉,使用ES6的Class繼承
class Animal {
constructor(aniamlType, age) {
this.aniamlType = aniamlType
this.age = age
}
eat() {
console.log('eat')
}
}
class Dog extends Animal {
constructor(aniamlType, age, name) {
super(aniamlType, age)
this.name = name
}
cat() {
console.log('cat')
}
}
var dog1 = new Dog('dog', '18', 'wb')
console.log(dog1.aniamlType, dog1.age, dog1.name)//dog 18 wb
dog1.cat()//cat
dog1.eat()//eat
console.log(dog1.constructor) //class Dog extends Animal {...}
可以看出ES6的class不用那么復雜了屋摇。直接使用,沒有后顧之憂