JS实现继承的几种方式

面试中,继承是一大类问题。本文总结了个人对继承的几种方式的理解,并分析了一些优缺点。

1.原型继承

//父类:Animal
function Animal(name){
	this.name=name || "Animal";
	this.master="铲屎官";
	this.sleep=function(){
		console.log(this.name+ "正在睡觉");
	}
}
//原型属性
Animal.prototype.age=1;
//原型方法
Animal.prototype.eat=function(food){
	console.log(this.name +"吃的食物是:"+food);
}

//子类:Cat
function Cat(){}
Cat.prototype=new Animal();
Cat.prototype.name="猫猫";
//可以添加子类自己的属性
Cat.prototype.leg=4;
//可以添加子类自己的方法
Cat.prototype.cry=function(){
	console.log("喵喵");
};
var cat=new Cat();
cat.sleep();  //猫猫正在睡觉
cat.age; //1
cat.eat("鱼"); //猫猫吃的食物是:鱼
cat.master; //"铲屎官"
cat.cry(); //喵喵

//实例测试
cat instanceof Cat; //true
cat instanceof Animal; //true
Cat.prototype instanceof Animal; //true

优点:是最纯粹的继承,实例既是父类Animal的实例,也是子类Cat/Dog的实例。

缺点:

    (1)子类的属性和方法,需要在new Animal()之后执行,且不能放进子类的构造函数中去(可以看到子类都是一个空的构造函数)。

    (2)子类实例共享父类引用属性,会造成相互影响。

    (3)无法实现多继承。

2.构造继承

//构造继承
//父类:Animal
function Animal(name){
	this.name=name || "Animal";
	this.master="铲屎官";
	this.sleep=function(){
		console.log(this.name+"正在睡觉");
	}
}
//原型属性
Animal.prototype.age=1;
//原型方法
Animal.prototype.eat=function(food){
	console.log(this.name+"吃的食物是:"+food);
}

//子类:Cat
function Cat(){
	Animal.call(this);
	this.name="猫猫";
	this.master="猫奴";
	//子类自己的属性
	this.leg=4;
	//子类自己的方法
	this.cry=function(){
		console.log("喵喵");
	}
}

var cat=new Cat();
cat.name; //"猫猫"
cat.master; //"猫奴"
cat.sleep(); //猫猫正在睡觉
cat.leg; //4
cat.cry(); //喵喵
//无法继承父类的原型属性和方法
cat.age; //undefined
cat.eat("鱼"); //Uncaught TypeError: cat.eat is not a function

//实例测试
cat instanceof Cat; //true
cat instanceof Animal; //false
Cat.prototype instanceof Animal; //false

核心:在子类构造函数中调用Animal.call(this)

优点:可以把子类的属性和方法写在子类的构造函数中,也解决了共享父类引用属性的问题,并且实现了多继承

缺点:

    (1)实例并不是父类的实例,只是子类的实例

    (2)只能继承父类的实例属性和方法,无法继承其原型属性和方法

    (3)父类的方法没有被共享,即有多少个子类,就有多少个父类实例函数的副本,影响性能。

3.实例继承

//实例继承
//父类:Animal
function Animal(name){
	this.name=name || "Animal";
	this.master="铲屎官";
	this.sleep=function(){
		console.log(this.name+"正在睡觉");
	}
}
Animal.prototype.age=1;
Animal.prototype.eat=function(food){
	console.log(this.name+"吃的食物是:"+food);
}

//子类:Cat
function Cat(){
	var instance=new Animal();
	instance.name="猫猫";
	instance.master="猫奴";
	instance.leg=4;
	instance.cry=function(){
		console.log("喵喵");
	}
	return instance;
}

var cat=new Cat();
cat.name; //"猫猫"
cat.master; //"猫奴"
cat.sleep(); //猫猫正在睡觉
cat.leg; //4
cat.cry(); //喵喵
cat.age; //1
cat.eat("鱼"); //猫猫吃的食物是:鱼

//实例测试
cat instanceof Cat; //false
cat instanceof Animal; //true
Cat.prototype instanceof Animal; //false

核心:在子类构造函数中实例化父类var instance=new Animal(),并返回这个实例。

优点:不限调用方式,既可以实例化子类new Cat(),也可以直接调用子类Cat(),其返回结果相同。并且继承了父类的原型属性和方法。

缺点:

    (1)实例是父类的实例,不是子类的实例

    (2)不支持多继承

4.拷贝继承

//父类:Animal
function Animal(name){
	this.name=name || "Animal";
	this.master="铲屎官";
	this.sleep=function(){
		console.log(this.name+"正在睡觉");
	}
}
Animal.prototype.age=1;
Animal.prototype.eat=function(food){
	console.log(this.name+"吃的食物是:"+food);
}

//子类:Cat
function Cat(){
	var animal=new Animal();
	for(var p in animal){
		Cat.prototype[p]=animal[p];
	}
	Cat.prototype["name"]="猫猫";
	this.leg=4;
	this.cry=function(){
		console.log("喵喵");
	}
}


var cat=new Cat();
cat.name; //"猫猫"
cat.master; //"铲屎官"
cat.age; //1
cat.sleep(); //猫猫正在睡觉
cat.eat("鱼"); //猫猫吃的食物是:鱼
cat.leg; //4
cat.cry(); //喵喵

//实例测试
cat instanceof Cat; //true
cat instanceof Animal; //false
Cat.prototype instanceof Animal; //false

优点:支持多继承

缺点:

    (1)无法获取父类不可枚举的方法

    (2)因为要拷贝父类的属性,效率较低

5.组合继承

//父类:Animal
function Animal(name){
	this.name=name || "Animal";
	this.master="铲屎官";
	this.sleep=function(){
		console.log(this.name+"正在睡觉");
	}
}
Animal.prototype.age=1;
Animal.prototype.eat=function(food){
	console.log(this.name+"吃的食物是:"+food);
}

//子类:Cat
function Cat(){
	Animal.call(this);
	this.name="猫猫";
	this.master="猫奴";
	this.leg=4;
	this.cry=function(){
		console.log("喵喵");
	}
}
Cat.prototype=new Animal();
Cat.prototype.constructor=Cat; //只要修改了prototype属性,就需要重置constructor属性

var cat=new Cat();
cat.name; //"猫猫"
cat.master; //"猫奴"
cat.age; //1
cat.sleep(); //猫猫正在睡觉
cat.eat("鱼"); //猫猫吃的食物是:鱼
cat.leg; //4
cat.cry(); //喵喵

//实例测试
cat instanceof Cat; //true
cat instanceof Animal; //true
Cat.prototype instanceof Animal; //true

核心:同时采用构造继承+原型继承

优点:可以继承父类原型方法和属性;实例既是子类的实例,也是父类的实例;实现了父类函数的共享

缺点:调用了两次父类构造函数,生成了两份实例

6.寄生组合继承

function Animal(name){
	this.name=name || "Animal";
	this.master="铲屎官";
	this.sleep=function(){
		console.log(this.name+"正在睡觉");
	}
}
Animal.prototype.age=1;
Animal.prototype.eat=function(food){
	console.log(this.name+"吃的食物是:"+food);
}

function Cat(){
	Animal.call(this); //构造继承,无法继承父类原型的方法和属性
	this.name="猫猫";
	this.leg=4;
	this.cry=function(){
		console.log("喵喵");
	}
}
//创建一个没有实例方法的类
function Super(){};
Super.prototype=Animal.prototype; //只继承父类原型的方法和属性
Cat.prototype=new Super();

var cat=new Cat();
cat.name; //"猫猫"
cat.master; //"铲屎官"
cat.leg; //4
cat.cry(); //喵喵
cat.sleep(); //猫猫正在睡觉
cat.age; //1
cat.eat("鱼"); //猫猫吃的食物是:鱼

//实例测试
cat instanceof Cat; //true
cat instanceof Animal; //true
Cat.prototype instanceof Animal; //true

核心:创建一个没有实例方法的类Super,将父类的原型赋予Super的原型

优点:堪称完美,共享了父类的方法,不会发生重复实例化父类的情况

缺点:实现有点复杂

7.ES6 class

class Animal{
	constructor(name,master,age){
		this.name=name || "Animal";
		this.master="铲屎官";
		this.age=1;
	}

	sleep(){
		console.log(this.name+"正在睡觉");
	}

	eat(food){
		console.log(this.name+"吃的食物是:"+food);
	}
}

class Cat extends Animal{
	constructor(){
		super();
		this.name="猫猫";
		this.master="猫奴";
		this.leg=4;
	}

	cry(){
		console.log("喵喵");
	}
}

let cat=new Cat();
cat.name; //"猫猫"
cat.master; //"猫奴"
cat.age; //1
cat.leg; //4
cat.sleep(); //猫猫正在睡觉
cat.eat("鱼"); //猫猫吃的食物是:鱼
cat.cry(); //喵喵

//实例测试
cat instanceof Cat; //true
cat instanceof Animal; //true

特点:和寄生组合继承的效果一致,但是简洁得多,且可读性好

需要注意的是:父类中的所有方法都是prototype上的方法,且不可枚举;子类必须在constructor中调用super()方法,否则实例化时会报错

















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值