阶段一
首先创建一个父类的函数方便继承
function Animal(aniamlType,age){
this.aniamlType = aniamlType
this.age = age
}
然后再创建一个子类用来继承
function Dog(aniamlType, age, name) {
Animal.call(this, aniamlType, age)
this.name = name
}
只需将this传进去执行一遍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
可以看到这样就能让实例得到父类的方法了。
但是当我再创建一个实例的时候,然后修改父类的时候,由于两个实例的proto是子类的prototype,而子类的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", "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会发生改变。
Dog.prototype.cat = function () {
console.log('cat')
}
console.log(Animal.prototype.cat) // function () { console('cat') }
console.log(dog1.constructor)//function Animal ....
在上面会发现创建实例的竟然是爷爷,而不是爸爸。这就乱套了。
所以这时候就需要将子类的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不用那么复杂了。直接使用,没有后顾之忧