JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。JS面向对象知识中,继承是比较难比较抽象的一块内容,而且实现继承有很多种方法,每种方法又各有优缺点,需要对面向对象知识中的对象、原型、原型链、构造函数等基础知识掌握透彻,下面我们介绍四种继承的方法:原型链继承,构造继承,实例继承,组合继承。
既然要实现继承,那么首先我们得有一个父类,代码如下:
/*先定义一个父类对象*/
function Animal(){ //动物类有什么特征和行为
//属性
this.name=null;
this.AnimalColor=null;
this.sex=null;
this.age=null;
this.type=null;
//实例方法
this.sleep=function (){
return this.name+"会睡觉";
}
}
// 原型方法 原型链追加(继承) 给原型方法里面追加属性和行为的
Animal.prototype.eat=function (){
return this.name+"会吃饭";
}
原型链继承
核心: 将父类的实例作为子类的原型
1.定义一个子类对象
//定义一个子类对象
function Cat(){
this.soloMouse=function (){ //子类特有行为
return this.name+"抓老鼠";
}
}
2.实例化子类对象
var a=new Animal();//实例对象
Cat.prototype=a;
Cat.prototype.speed=function (){
return 5.0;
}
//实例化cat对象
var c=new Cat();
c.name="小花";
c.type="猫科";
c.AnimalColor="白黑";
c.age="2";
c.sex="母";
/* c.prototype.name="小黑";*/
console.log(c);
console.log(c.sleep());
console.log(c.soloMouse());
console.log(c.eat());
console.log(c.speed());
console.log(c instanceof Cat); //true
console.log(c instanceof Animal); //true
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在
new Animal()
这样的语句之后执行。 - 无法实现多继承
- 来自原型对象的所有属性被所有实例共享
- 创建子类实例时,无法向父类构造函数传参
构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
//定义一个父类
function rou(name){
this.name=name;
this.eat=function (){
return this.name+"吃肉";
}
}
//定义一个子类对象 同时实现继承
function Cat(name,color,sex,age,type){
Animal.call(this,name,color,sex,age,type);
/* Animal.apply(this,[name,color,sex,age,type]);*/
/*this.name=name;*/
rou.apply(this,[name]);
/* call apply 在args上有区别call 序列传参 apply 为数组传参*/
var c=new Cat("小猫","白色","母",2,"猫");
console.log(c);
console.log(c.eat());
console.log(c instanceof Cat); //true
console.log(c instanceof Animal); //false
console.log(c instanceof rou); //false
}
特点:
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
实例继承
核心:为父类实例添加新特性,作为子类实例返回
//定义一个子类
function Cat(){
var a=new Animal();
a.name="小猫";
return a; //不支持多继承
}
//实例化子类
var c=new Cat(); //不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
console.log(c);
console.log(c.speed());
console.log(c instanceof Cat); // false 实例是父类的实例,不是子类的实例
console.log(c instanceof Animal); //true
特点:
- 不限制调用方式,不管是
new 子类()
还是子类()
,返回的对象具有相同的效果
缺点:
- 实例是父类的实例,不是子类的实例
- 不支持多继承
组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function rou(){
this.eat=function (){
return "吃肉";
}
}
//定义一个子类对象
function Cat(){
Animal.call(this); //构造继承
rou.call(this);
}
Cat.prototype=new Animal(); //原型链继承
var c=new Cat();
console.log(c);
console.log(c.speed());
console.log(c.eat());
特点:
- 弥补了构造继承的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)