目录
1.原型链继承
原型链继承是通过将一个构造函数的实例赋值给另一个构造函数,这样第二个构造函数就能访问到第一个构造函数的实例,而且能通过第二个构造函数的实例来读取和修改第一个构造函数的实例,作为自己实例的值
let A = function(name,age){ //构造方法A
this.name = name
this.age = age
this.say = function(){
return `你好,我叫${this.name},今年${this.age}岁了!`
}
}
A.prototype.eat = function(){}
let B = function(){} //构造方法B
B.prototype = new A('小明',18) //让B的原型属性等于A的实例,这样B的实例通过__proto__链就能访问到A的实例的内容
let b = new B()
console.log(b)
可以看到B的实例上面并没有任何属性和方法,它是通过__proto__找到了A的实例,将A的实例的属性和方法作为自己的属性和方法,并且还可以读取到A的实例上的方法,这时我们打印b的name和age,就会如图
但是,原型链继承有个缺点,如果A定义了一个引用数据类型,比如数组
let A = function(name,age){ //构造方法A
this.name = name
this.age = age
this.hobby = ['唱歌','画画'] //引用数据类型
this.say = function(){
return `你好,我叫${this.name},今年${this.age}岁了!`
}
}
那么如果B的一个实例对这个引用数据类型做了更改,其他实例去访问这个引用数据类型,拿到的就是更改后的,比如现在new两个B的实例
let b1 = new B()
let b2 = new B()
b1.hobby.pop()
console.log(b1)
console.log(b2)
得到的结果如下
因为栈里面保存的是引用数据类型在堆里面的地址,两个B的实例对象的hobby是同一个,所以更改一个其他的也会更改
2.构造函数继承
为了避免继承后引用数据类型的错误,又引出了构造函数继承。构造函数继承就是通过修改某一个构造函数的this指向,来给另一个函数添加属性和方法
let A = function(name,age){ //构造方法A
this.name = name
this.age = age
this.hobby = ['唱歌','画画']
}
A.prototype.eat = function(){}
let B = function(name,age){ //构造方法B
A.call(this,name,age) //将A的this指向改为B,这样每次new B,都会转去执行A的构造函数来给B的实例添加name,age,hobby属性
}
let b = new B('小花',13)
console.log(b)
可以发现构造函数继承能解决原型链继承中引用数据类型的不足,我们用构造函数集成的时候,改变一个实例的引用数据类型,其他实例是不受影响的
由于改变了this指向,所以new B的时候,无法访问到A的原型属性上的方法和属性,所以B的实例里面没有A的原型属性上的东西
3.组合继承
因为前两种方法都有优缺点,所以将两者结合起来
let A = function(name,age){ //构造方法A
this.name = name
this.age = age
this.hobby = ['唱歌','画画']
}
A.prototype.eat = function(){}
let B = function(name,age){ //构造方法B
A.call(this,name,age) //将A的this指向改为B,这样每次new B,都会转去执行A的构造函数来给B的实例添加name,age,hobby属性
}
B.prototype = new A()
let b = new B('小花',13)
console.log(b)
可以发现现在B的实例也可以读取到A的原型属性上的东西,但是B的prototype很冗余,没必要将A的全部包含进去,只需要包含A的原型上的东西就行了,所以就有了寄生组合继承
4.寄生组合继承
寄生组合继承就是再声明一个构造函数,用于接收A原型上的东西,然后让B的原型等于新的构造函数的实例
let A = function(name,age){ //构造方法A
this.name = name
this.age = age
this.hobby = ['唱歌','画画']
}
A.prototype.eat = function(){}
let B = function(name,age){ //构造方法B
A.call(this,name,age) //将A的this指向改为B,这样每次new B,都会转去执行A的构造函数来给B的实例添加name,age,hobby属性
}
let Fn = function(){}
Fn.prototype = A.prototype
B.prototype = new Fn() //这两句也可以合并为B.prototype = Object.create(A.prototype)
let b = new B('小花',13)
console.log(b)
这样B的原型上就不会出现A自身的属性和方法
5.类继承
class A{
constructor(name){
this.name = name
}
}
class B extends A{
constructor(name,age){
super(name)
this.age = age
}
}
let b = new B('小红',18)
console.log(b)