为何要继承
希望子类对象拥有父类的属性和方法
构造函数的继承
call写在子类构造函数中,将父构造函数中this指向由window指向子对象,并且执行父构造函数中的代码,(其实就相当于将父构造函数中的代码复制一份到子类构造函数中,将整个看做是子类构造器中的内容),所以改变了this指向,也继承了父类构造函数中的代码
function People(){
this.legs = 2;
}
function Man(){
People.call(this);
this.run = 3;
}
let m1 = new Man();
复制代码
call方法和apply方法的区别:从第二个参数起,call直接将这些参数依次传递给父构造函数,apply将这些参数放入数组中在传递给父构造函数。
function People(name,legs){
this.name = name;
this.legs = legs;
}
function Man(name,legs){
People.call(this,name,legs);
//People.apply(this,[name,legs]);
this.run = 3;
}
let m1 = new Man('yr', 2);
复制代码
注意: call和apply继承的只是构造函数中的属性和方法,通过原型prototype给父类添加的是无法继承的。
原型继承1:子类prototype直接引用父类prototype
function People(name,legs){
this.name = name;
this.legs = legs;
}
People.prototype.showName = function(){
console.log(this.name)
}
function Man(){
this.run = 3;
}
Man.prototype = People.prototype;
Man.prototype.showLegs= function(){
console.log(this.legs)
}
let m1 = new Man('yr', 2);
复制代码
这种原型继承有个问题:就是直接将子类的prototype指向父类的prototype,这时他们两个使用的用一个内存地址,我们在子类Man的prototype上添加的showLegs也会添加在父类People的prototype上,显然这种方式是不合适的。
原型继承2:拷贝继承
function People(name,legs){
this.name = name;
this.legs = legs;
}
People.prototype.showName = function(){
console.log(this.name)
}
function Man(name,legs){
People.call(this,name,legs)
this.run = 3;
}
Man.prototype.showName= function(){
console.log(this.legs)
}
Man.prototype = {
...People.prototype,
...Man.prototype
}
let m1 = new Man('yr',18);
let m2 = new Man('lyl',20);
复制代码
拷贝继承,使用扩展运算符将父类原型与子类原型合并,属性相同时,子类为准 (扩展运算符属性相同时,后面的覆盖前面的)
原型继承3:继承父类实例化对象
function People(name,legs){
this.name = 'yr';
this.legs = 2;
}
People.prototype.showName = function(){
console.log(this.name)
}
function Man(){
this.run = 3;
}
Man.prototype = new People();
Man.prototype.showLegs= function(){
console.log(this.legs)
}
let m1 = new Man();
复制代码
这种子类继承了父类构造函数实例化对象,可以获得父类构造函数和原型上的属性和方法。但是存在一个问题,如果父类构造函数中包含引用类型值(数组,对象那类),就会出现以下问题
People的每个实例都有names属性,Man.prototype = new People();就相当于Man.prototype成为了People的一个实例,就像是创建了Man.prototype.names,那么通过Man创建出来的实例都会共享这个属性。
function People(name,legs){
this.names = ['yr','lyl'];
this.legs = 2;
}
People.prototype.showName = function(){
console.log(this.name)
}
function Man(){
this.run = 3;
}
Man.prototype = new People();
Man.prototype.showLegs= function(){
console.log(this.legs)
}
let m1 = new Man();
m1.names.push = 'xn';
let m2 = new Man();
复制代码
此时我们需要的m2是不应该有新添加的xn,并且这种继承不能像父类构造函数传参
组合式继承
通过call或者apply继承构造函数,prototype继承父级的prototype
function People(name,legs){
this.names = name;
this.legs = legs;
}
People.prototype.showName = function(){
console.log(this.name)
}
function Man(name,legs){
People.call(this,name,legs)
this.run = 3;
}
Man.prototype = new People();
Man.prototype.constructor = Man;
Man.prototype.showLegs= function(){
console.log(this.legs)
}
let m1 = new Man('yr',18);
let m2 = new Man('lyl',20);
复制代码
组合式继承的原型链是
总结
当子类的prototype继承父类实例化对象时,原型链才发生真正的变化。