//父类
function Animal(name){
this.type="Animal";
this.name=name || "动物";
this.sleep=function(){
console.log(this.name+"正在睡觉")
}
}
Animal.prototype.eat=function(food){
console.log(this.name+"正在吃"+food)
}
#0001 原型链继承
//原型链继承
//原型链继承
function Cat(name){
this.name = name;
}
Cat.prototype=new Animal();
Cat.prototype.constructor=Cat;//关键代码this指向Cat
//如果constructor不指向Cat那么就会指向父类
let cat=new Cat("罗小黑");
//继承的属性
console.log(cat.type);
console.log(cat.name);
console.log(cat.sleep());
console.log(cat.eat("大餐"))
优点
- 实现简单
- 继承关系纯粹
console.log(cat instanceof(Cat))//true console.log(cat instanceof(Animal))//true
- 可以直接访问父类原型链属性和函数
Animal.prototype.run=function(){ console.log(this.name+"正在跑路") } Animal.prototype.size="big" console.log(cat.run())//罗小黑正在跑路 console.log(cat.size)//big
缺点
- 子类所有属性将共享父类属性
- 创建子类实例时无法向父类构造函数传递参数
- 无法实现多继承
#0002 构造继承
//构造继承
//主要思想是通过call改变this指向,调用父类构造函数,从而将父类实例绑定到子类
console.log("构造继承")
function Cat(name){
Animal.call(this);//核心代码
this.name = name || "Tom";
}
let cat=new Cat("Tommy")
console.log(cat.type);//Animal
console.log(cat.name);//Tommy
console.log(cat.sleep());//Tommy正在睡觉
console.log(cat.eat("大餐"))//cat.eat is not a function
//无法调用父类原型对象上的函数
//只是重新绑定this指向,没有调用父类原型对象上的函数
console.log(cat instanceof(Cat))//true
console.log(cat instanceof(Animal))//false
优点
可以解决子类实例共享父类属性的问题 调用后的this指向子类Cat,相当于将父类的属性直接绑定到子类的this中,这样每个子类都拥有自己的继承过来的属性,彼此之间相互独立
创建子类的实例后可以向父类传递参数
function Cat(name,parentAge){ Animal.call(this,parentAge); this.name=name; } let cat=new Cat("tommy",11); console.log(cat.age)//11
- 可以实现多继承(调用call函数)
缺点
- 因为是直接绑定的this,造成子类和父类没有关系,因此不是真正的继承
console.log(cat instanceof(Cat))//true console.log(cat instanceof(Animal))//false
- 只能继承父类属性,不能继承原型链上的属性和函数
- 无法复用父类的实例函数(每个子类都会拥有父类的实例,影响内存)
#0003 复制继承
主要思想是遍历父类实例的属性和函数,并将其设置为子类的属性和函数
function Cat(name,age){
let animal=new Animal(name,age);
for(let key in animal){//核心代码
if(animal.hasOwnProperty(key)){
this[key]=animal[key];
}else{
Cat.prototype[key]=animal[key];
}
}
}
Cat.prototype.eat = function(food){
console.log(this.name+"正在吃"+food);
}
let cat=new Cat("狗蛋",3);
console.log("复制继承")
console.log(cat.type);//Animal
console.log(cat.name);//狗蛋
console.log(cat.age)//3
console.log(cat.sleep());//狗蛋正在睡觉
console.log(cat.eat("大餐"))//狗蛋正在吃大餐
console.log(cat instanceof(Cat))//true
console.log(cat instanceof(Animal))//false
优点
- 支持多继承
- 能够同时继承实例属性和原型对象上的属性和函数
- 可以向父类构造函数中传值
缺点
- 父类所有属性子类都有一份因此会消耗内存
- 实例只是子类的实例,并不是父类的实例
#0004 组合继承
组合继承总结了构造继承和原型继承,修改prototype的同时也绑定了this
//组合继承
//组合继承总结了构造继承和原型继承的两种方法
function Cat(name,age){
Animal.call(this);
this.name=name;
this.age=age;
}
//核心代码
Cat.prototype=new Animal();
Cat.prototype.constructor=Cat;
let cat=new Cat("卡布达",12);
console.log("组合继承")
console.log(cat.type);//Animal
console.log(cat.name);//卡布达
console.log(cat.age)//12
console.log(cat.sleep());//卡布达正在睡觉
console.log(cat.eat("大餐"))//卡布达正在吃大餐
console.log(cat instanceof(Cat))//true
console.log(cat instanceof(Animal))//true
优点
- 既能访问实例属性也能访问原型链属性
- 既是子类实例也是父类实例
- 没有属性共享的问题
- 可以向父类构造函数传递参数
缺点
父类属性会绑定两次(call绑定一次,改写子类prototype),造成性能浪费
寄生组合继承
本质是对组合继承的优化,通过一个立即执行函数使得只取父类prototype属性,过滤掉父类实例属性
function Cat(name,age){
Animal.call(this);
this.name=name;
this.age=age;
}
(function(){
let Super=function(){}
Super.prototype=Animal.prototype;
Cat.prototype=new Super();
Cat.prototype.constructor=Cat;
})()
let cat=new Cat("蟑螂恶霸",12)
console.log("寄生组合继承")
console.log(cat.type);//Animal
console.log(cat.name);//蟑螂恶霸
console.log(cat.age)//12
console.log(cat.sleep());//蟑螂恶霸正在睡觉
console.log(cat.eat("大餐"))//蟑螂恶霸正在吃大餐
console.log(cat instanceof(Cat))//true
console.log(cat instanceof(Animal))//true