JavaScript面向对象:类的几种继承方式


前端小白记录学习笔记,不做他用。

类与实例

类的声明

·

  1. 用构造函数模拟类(传统写法)

function Person1(name){
this.name=name;
}

  1. 用class声明(ES6写法)

class Person2{
construtor(name){
this.name=name;
}
}

控制台输出:
自制截图,哼!

生成实例

类的实例化很简单,直接new就可以。
自制截图,哼!

类与继承

如何实现继承

继承的本质就是原型链。

继承的几种方式

  1. 借助构造函数
function Parent1(){
	this.name="parent莉莉";
}
function Child1(){
	Parent1.call(this);//改变this的指向,非常重要。用apply或者call都行
	this.type="child莉莉";
}
console.log(new Child1);

在上面的代码中,最重要的是在子类的构造函数中写了 Parent1.call(this);,这样是为了让Parent1的构造函数在Child1的构造函数中执行。this的指向发生改变,Parent的实例——>改为指向Child1的实例,导致Parent1的实例属性挂到Child1的实例上,这样就实现了继承。
控制台打印结果:
自制截图,哼!
上面截图表明:Child1先有Parent1实例的属性,再有Child1实例的属性。
分析
这种方式虽然改变了this的指向,但是Child1无法继承Parent1的原型。也就是说,如果我给Parent1的原型添加一个方法:

Parent1.prototype.say=function(){...};

这个方法是无法被Child1继承的。

  1. 通过原型链实现继承
function Parent(){
	this.name="parent属性";
}
function Child(){
	this.type="child属性";
}
Child.prototype = new Parent();//原型链
console.log(new Child);

上方代码最重要的是Child.prototype = new Parent();。每个函数都有prototype属性,构造函数也有这个属性,这个属性是一个对象。我们用这行代码Child.prototype = new Parent();把Parent的实例赋给Child的prototype,从而实现继承。Child的实例,Child构造函数,Parent的实例构成一个三角关系,从而,new Child.__proto__===new Parent() =true;
分析
这种继承方式,Child可以继承Parent原型,但是有个缺点:如果修改了child1实例的name属性,那么child2实例的name属性值也会发生变化,显然这样不太好。
在实际业务中,两个子模块对象应该隔离开才对。如果修改一个对象,另一个对象也发生改变,这样对业务设计不太好。造成这种缺点的原因是child1和child2共用原型,也就是说child1.__proto__===child2.__proto__的结果为true。

  1. 组合的方式:构造函数+原型链
//组合方式实现继承:构造函数+原型链
function Parent2(){
	this.name="parent属性";
	this.arr=['1','2','3'];
}
function Child2(){
	Parent2.call(this);//执行parent方法
	this.type="child属性";
}
Child2.prototype=new Parent2();//第二次执行parent方法
var child = new Child2();

这种组合的方式能够解决以上两种继承方式的缺点:既能继承原型的属性内容,也不会造成原型属性的修改。

  1. 原型式继承
    不用严格意义上的构造函数,借助原型可以根据已有的对象创建新对象,而且不必因此创建自定义类型。
function Object(obj){
	function Func(){};
	Func.prototype = o;
	return new Func();
}

从本质上讲,Object对传入其中的对象做了一次浅复制。举个例子:

var person={
	name:'Lily',
	friends:['ann','julia']
}
var onePerson=Object(person);
onePerson.name='jun';
onePerson.friends.push('jessica');

var anotherPerson = Object(person);
anotherPerson.name='ton';
anotherPerson.friends.push('jennie');

console.log(person.friends);
//['ann','julia','jessica','jennie']

在这个例子中,相当于创建了person的两个副本。

  1. 寄生式继承
    寄生式继承是与原型式继承紧密相关的一种思路,它创造一个仅用于封装继承过程的函数,在函数内部增强对象,最后再返回对象。
function CreatePerson(origin){
	var clone = Object(origin);
	clone.say=function(){
		console.log('hello');
	};
	return clone;
}

缺点:使用寄生式继承来为对象添加函数,会因为函数不可复用而降低效率,这个与构造函数继承类似。

  1. 寄生组合式继承
    基本思想:通过借用构造函数来继承属性,通过原型链的混成方法来继承方法,不必为了指定子类型的原型而调用超类型的构造函数,只需要超类型的一个副本。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类的原型。
function inherit(sub,super){
	var obj = Object(super);//创建对象
	obj.constructor=sub;//增强对象
	sub.prototype=obj;//指定对象
}

因此,前面的例子可以改成以下形式:

function super(name){
	this.name=name;
	this.colors=['red','green'];
}
super.prototype.sayName=function(){
	console.log(this.name)
}
//继承属性
function sub(name,age){
	super.call(this,name);
	this.age=age;
}
//继承方法
inherit(sub,super);
sub.prototype.sayAge=function(){
	console.log(this.age);
}

优点:只调用一次super构造函数,因此避免了在sub.prototype上创建不必要的、多余的属性,与此同时,原型链还能保持不变,还能正常使用instanceof和isPrototypeOf(),因此,寄生组合式继承被认为是引用类型最理想的继承范式。

以上六种方式是ES5继承。ES5的继承可以用下图概括

转载自网络,侵权联系删除
ES6继承主要注意class的继承。class之间通过extends关键字实现继承,这比修改原型链实现继承要好。

class ColorPoint extends Point{
	constructor(x,y,color){
		super(x,y);
		this.color=color;
	}
	toString(){
		return this.color+' '+super.toString();
	}
}

注意:子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。因此,只有调用super之后,才可以使用this关键字。
一条继承语句同时存在两条继承链:一条继承属性,一条继承方法。

class A extends B{};
A.__proto__=B;//继承属性
A.prototype.__proto__=B.prototype;//继承方法

ES6的继承可以用下图概括:
转载自网络,侵权联系删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值