18910140161

js实现继承的几种方式

顺晟科技

2021-06-16 10:57:56

339

JS是面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。

JS继承的实现

既然我们想实现继承,我们首先必须有一个带有以下代码的父类:

//定义动物类别

功能动物(名称){

//属性

this.name=name | | ' Animal

//示例方法

this.sleep=function(){

Console.log(this.name '正在休眠!);

}

}

//原型法

动物。原型。吃=功能(食物){

Console.log(this.name '正在吃:'食物');

};

1.原型链继承

核心:将父类的实例作为子类的原型

函数Cat(){

}

cat . prototype=new Animal();

cat . prototype . name=' cat ';

//测试代码

var Cat=new Cat();

console . log(cat . name);

console . log(cat . eat(' fish '));

console . log(cat . sleep());

控制台日志(动物的猫实例);//true

console.log(cat的cat实例);//true

特点:

一种非常纯粹的继承关系,实例是子类的实例和父类的实例

父类添加原型方法/原型属性,子类可以访问

简单易实现

缺点:

如果想给子类添加属性和方法,必须在像new Animal()这样的语句之后执行它们,并且不能将它们放在构造函数中

多重继承无法实现

原型对象的引用属性由所有实例共享(参见附录代码:示例1了解详细信息)

创建子类实例时,不能将参数传递给父类构造函数

推荐指标:(3,4个致命缺陷)

2.结构遗传

核心:使用父类的构造函数来增强子类实例相当于将父类的实例属性复制到子类中(不使用原型)

功能类别(名称){

Animal.call(这个);

this.name=name | | ' Tom

}

//测试代码

var Cat=new Cat();

console . log(cat . name);

console . log(cat . sleep());

控制台日志(动物的猫实例);//false

console.log(cat的cat实例);//true

特点:

解决了1中子类实例共享父类引用属性的问题

创建子类实例时,可以将参数传递给父类

可以实现多重继承(调用多个父类对象)

缺点:

实例不是父类的实例,而是子类的实例

您只能继承父类的实例属性和方法,而不能继承原型属性/方法

无法实现函数重用,每个子类都有父类实例函数的副本,影响性能

推荐指数:(缺点3)

3.实例继承

核心:向父类实例添加新特性,并将其作为子类实例返回

功能类别(名称){

var instance=new Animal();

instance.name=name | | ' Tom

返回实例;

}

//测试代码

var Cat=new Cat();

console . log(cat . name);

console . log(cat . sleep());

控制台日志(动物的猫实例);//true

console.log(cat的cat实例);//false

特点:

调用模式没有限制,无论是新子类()还是子类(),返回的对象都有同样的效果

缺点:

实例是父类的实例,而不是子类的实例

不支持多重继承

推荐指数:

4.复制继承

功能类别(名称){

var Animal=new Animal();

for(var p in animal){

cat . prototype[p]=animal[p];

}

cat . prototype . name=name | | ' Tom ';

}

//测试代码

var Cat=new Cat();

console . log(cat . name);

console . log(cat . sleep());

控制台日志(动物的猫实例);//false

console.log(cat的cat实例);//true

特点:

支持多重继承

缺点:

低效率和高内存消耗(因为复制父类的属性)

无法获取父类的不可枚举方法(不可枚举方法,不能通过在中使用for来访问)

推荐指标:(缺点1)

5.组合遗传

核心:通过调用父类构造,继承父类的属性并保留传递参数的优势,然后以父类实例为子类原型实现函数重用

功能类别(名称){

Animal.call(这个);

this.name=name | | ' Tom

}

cat . prototype=new Animal();

组合继承也是由需要修复的构造函数指向的。

Cat . prototype . constructor=Cat;

//测试代码

var Cat=new Cat();

console . log(cat . name);

console . log(cat . sleep());

控制台日志(动物的猫实例);//true

console.log(cat的cat实例);//true

特点:

它弥补了模式2的缺陷,可以继承实例属性/方法和原型属性/方法

它是子类和父类的实例

不存在引用属性共享问题

可转移参数

功能是可重用的

缺点:

父类构造函数被调用两次,生成两个实例(子类实例屏蔽子类原型上的实例)

推荐索引:(只多消耗一点内存)

6.寄生组合遗传

核心:通过寄生切断父类的实例属性,这样当父类的构造被调用两次时,实例方法/属性就不会被初始化两次,避免了组合继承的缺点

功能类别(名称){

Animal.call(这个);

this.name=name | | ' Tom

}

(function(){

//创建一个没有实例方法的类

var Super=function(){ };

超级原型=动物原型;

//使用实例作为子类的原型

cat . prototype=new Super();

})();

//测试代码

var Cat=new Cat();

console . log(cat . name);

console . log(cat . sleep());

控制台日志(动物的猫实例);//true

console.log(cat的cat实例);//true

Cat . prototype . constructor=Cat;//需要修复下级构造函数

特点:

完美的

缺点:

实现更加复杂

推荐指标:(实施复杂,扣一星)

附录代码:

例1:

功能动物(名称){

//属性

this.name=name | | ' Animal

//示例方法

this.sleep=function(){

Console.log(this.name '正在休眠!);

}

//实例引用属性

this . features=[];

}

功能类别(名称){

}

cat . prototype=new Animal();

var Tom=new Cat(' Tom ');

var kissy=新猫(' Kissy ');

console . log(Tom . name);//'动物'

console . log(kissy . name);//'动物'

console . log(Tom . features);//[]

console . log(kissy . features);//[]

汤姆。名字='汤姆-新名字';

Tom . features . push(' eat ');

//对父类实例的值类型成员的更改不会影响

console . log(Tom . name);//'汤姆-新名字'

console . log(kissy . name);//'动物'

//对父类实例的引用类型成员的更改将影响其他子类实例

console . log(Tom . features);//['吃']

console . log(kissy . features);//['吃']

原因分析:

要点:属性搜索过程

执行tom.features.push,首先找到tom对象的实例属性(找不到),

然后转到原型对象,即动物实例。如果找到了,那么它就直接在对象的

在特征属性中插入一个值。

in console . log(kissy . features);当时。同上,没有kissy实例,去原型。

如果它碰巧存在于原型上,它将被直接返回,但是请注意,这个原型对象中的特征的属性值已经改变。

我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航