1.原型链继承
优点:共享了父类构造函数的say方法
缺点1:不能向父类构造方法传参
缺点2:子类实例共享了父类构造函数的引用属性,比如arr属性
/*
原型链继承
*/
function Parent(name){
this.name=name; //父亲
this.arr=[1]
}
Parent.prototype.say=function(){
console.log('hello')
}
function Child(like){
this.like=like;
}
Child.prototype=new Parent();
Child.prototype.constructor=Child
let boy1=new Child();
let boy2=new Child();
//优点:共享了父类构造函数的say方法
console.log(boy1.say(),boy2.say(),boy1.say===boy2.say)//undefined,undefined, true
//缺点1:不能向父类构造方法传参
console.log(boy1.name,boy2.name,boy1.name===boy2.name);//父亲,父亲,true
//缺点2:子类实例共享了父类构造函数的引用属性,比如arr属性
boy1.arr.push(2);
//修改boy1的arr属性,boy2的arr属性,也会变化,因为两个实例的原型上有了父类构造函数的实例属性arr;
console.log(boy2.arr)//[1,2]
//注意:修改boy1的name属性,是不会影响到boy2.name,因为设置boy1.name相当于在子类实例上新增了name属性
2.构造函数继承
优点1:可向父类构造函数传参
优点2:不共享父类构造函数的引用类型
缺点1:方法不能复用
缺点2:不能继承父类原型上的方法
/*
构造函数继承
*/
function Parent(name) {
this.name = name;
this.arr = [1];
this.say = function () {
console.log('hello')
}
}
function Child(name,like) {
Parent.call(this,name);//拷贝了父类的实例属性和方法
this.like = like;
}
//优点1:可向父类构造函数传参
let boy1 = new Child('小红','apple');
let boy2 = new Child('小明','orange');
//优点2:不共享父类构造函数的引用类型
boy1.arr.push(2);
console.log(boy2.arr) //[1]
//缺点1:方法不能复用
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say) //hello,hello,false
//缺点2:不能继承父类原型上的方法
Parent.prototype.walk=function(){
console.log('我会走路')
}
boy1.walk;//undefined (说明实例不能获得父类原型上的方法)
3.组合继承
核心:通过调用父类的构造函数,继承父类的属性并保留传参的特点,然后通过将父类实例作为子类原型,实现函数复用
优点1:可向父类构造函数传参
优点2:不共享父类构造函数的引用类型
优点3:方法可以复用
缺点1:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
/*
组合继承
*/
function Parent(name) {
this.name = name;
this.arr = [1];
}
Parent.prototype.say = function () {
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name);//拷贝了父类的实例属性和方法
this.like = like;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child
//优点1:可向父类构造函数传参
let boy1 = new Child('小红','apple');
let boy2 = new Child('小明','orange');
//优点2:不共享父类构造函数的引用类型
boy1.arr.push(2);
console.log(boy2.arr) //[1]
//优点3:方法可以复用
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say) //hello,hello,true
//缺点1:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
组合继承优化1 解决了:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
/优点1:可向父类构造函数传参
优点2:不共享父类构造函数的引用类型
优点3:方法可以复用
缺点1:当修复子类构造函数的指向后,父类实例的构造函数指向也会跟着变了
/*
组合优化继承
解决了:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
*/
function Parent(name) {
this.name = name;
this.arr = [1];
}
Parent.prototype.say = function () {
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name);//拷贝了父类的实例属性和方法
this.like = like;
}
Child.prototype = Parent.prototype
Child.prototype.constructor = Child //修复子类的构造函数指向
优点1:可向父类构造函数传参
let boy1 = new Child('小红','apple');
let boy2 = new Child('小明','orange');
let p1=new Parent('爸爸')
优点2:不共享父类构造函数的引用类型
boy1.arr.push(2);
console.log(boy2.arr) //[1]
优点3:方法可以复用
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say) //hello,hello,true
缺点1:当修复子类构造函数的指向后,父类实例的构造函数指向也会跟着变了
console.log(p1.constructor) //Child
没修复之前:console.log(boy1.constructor)//Parent
修复代码:Child.prototype.constructor = Child
修复之后:console.log(boy1.constructor)//child
console.log(p1.constructor) //Child(我们希望是parent)
具体原因:修复的Child.prototype和Parent.prototype都是引用类型,指向同一块地址,对Child.prototype更改会引起Parent.prototype的修改。
4.寄生组合继承 对上述缺点进行优化 完美!
/*
组合优化继承
解决了:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
*/
function Parent(name) {
this.name = name;
this.arr = [1];
}
Parent.prototype.say = function () {
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name);//拷贝了父类的实例属性和方法
this.like = like;
}
Child.prototype = Object.create(Parent.prototype) //关键代码
Child.prototype.constructor = Child //修复子类的构造函数指向
//优点1:可向父类构造函数传参
let boy1 = new Child('小红','apple');
let boy2 = new Child('小明','orange');
let p1=new Parent('爸爸')
//优点2:不共享父类构造函数的引用类型
boy1.arr.push(2);
console.log(boy2.arr) //[1]
//优点3:方法可以复用
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say) //hello,hello,true
console.log(p1.constructor) //Parent
*/