问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

javascript多种继承方式及其优缺点

发布网友 发布时间:2024-10-03 01:21

我来回答

1个回答

热心网友 时间:2024-11-17 06:20

前言

本文介绍了javascript中多种继承方式及其优缺点,读完本文将收获:

知道javascript中6种继承方式的实现原理

知道各种继承方式的优缺点

理解各种继承方式之间的联系

一、原型链继承

顾名思义,原型链继承利用了构造函数的原型对象来实现继承。如下代码中有两个类,子类Child想要继承父类Father的name属性该怎么做?

function?Father(){????this.name?=?'William'}function?Child(){}

每一个构造函数都有一个prototype属性,它指向构造函数的原型对象。我们可以让子类构造函数的prototype属性等于父类的一个实例,即:

Child.prototype?=?new?Father()

然后我们生成一个子类的实例,并且去访问子类实例的name属性:

let?child1?=?new?Child()console.log(child1.name)????//?Willam

可以看到打印出William,访问child1的name属性的工作机制是这样的:

查找child1自身发现没有name属性

查找child1.__proto__属性指向的对象,该对象也就是Child.prototype指向的对象,它是父类Father的一个实例,查找该对象,发现有name属性,于是返回。用一个图来表示上面的关系:途中红色箭头部分就是所谓的原型链,利用原型链我们也可以实现方法的继承,如下所示:

Father.prototype.getName?=?function?()?{????console.log(this.name)}child1.getName()??//?william

上面示例的整体代码:

function?Father()?{????this.name?=?'Willam'}Father.prototype.getName?=?function?()?{????console.log(this.name)}function?Child()?{}Child.prototype?=?new?Father()let?child1?=?new?Child()console.log(child1.name)child1.getName()?//?william

利用原型链实现继承简单直观,那么修改子类实例的属性会怎样呢?

let?child2?=?new?Child()console.log(child2.name)????//?Williamconsole.log(child1.name)????//?Williamchild2.name?=?'Jane'console.log(child2.name)????//?Janeconsole.log(child1.name)????//?William

看上面代码似乎没有问题,实例之间的属性并没有相互影响,那是因为这里是直接给child2增加了一个自身的name属性,而不是去修改它继承的那个属性:

console.log(child2.__proto__.name)??//?William

如果所继承的属性是引用类型,那修改实例属性时就需要注意了:

function?Father()?{????this.names?=?['William']}function?Child()?{}Child.prototype?=?new?Father()let?child1?=?new?Child()let?child2?=?new?Child()console.log(child1.names)?//?[?'William'?]console.log(child2.names)?//?[?'William'?]child1.names.push('Jane')console.log(child1.names)?//?[?'William',?'Jane'?]console.log(child2.names)?//?[?'William',?'Jane'?]二、借用构造函数(经典继承)

直接在子类构造函数中调用父类构造函数并改变其this指向。

function?Father()?{????this.names?=?['William',?'Jane']????this.getName?=?function?()?{????????console.log(this.names)????}}function?Child()?{????Father.call(this)}let?child1?=?new?Child()let?child2?=?new?Child()console.log(child1.names)?//?[?'William',?'Jane'?]console.log(child2.names)?//?[?'William',?'Jane'?]child1.getName()??//?[?'William',?'Jane'?]child2.getName()??//?[?'William',?'Jane'?]child1.names.pop()console.log(child1.names)?//?[?'William'?]console.log(child2.names)?//?[?'William',?'Jane'?]child1.getName()??//?[?'William'?]child2.getName()??//?[?'William',?'Jane'?]

优点:

子类实例的方法和属性都是独有的

可以向父类构造函数传参,举个例子

function?Father(name)?{????this.name?=?name}function?Child(name)?{????Father.call(this,?name)}let?child1?=?new?Child('William')console.log(child1.name)??//?William

缺点:

在每一次创建子类实例时,继承的方法总是要被重新创建。

三、组合继承

组合继承是javascrip最常用的继承模式,它将一和二两种方法组合起来:

利用原型链继承方法

借助构造函数继承属性

function?Father()?{????this.names?=?['William',?'Jane']}Father.prototype.getNames?=?function?()?{????console.log(this.names)}function?Child()?{????Child.prototype?=?new?Father()8}Child.prototype?=?new?Father()Child.prototype.constructor?=?Childlet?child1?=?new?Child()let?child2?=?new?Child()console.log(child1.names)?//?[?'William',?'Jane'?]console.log(child2.names)?//?[?'William',?'Jane'?]child1.getNames()?//?[?'William',?'Jane'?]child2.getNames()?//?[?'William',?'Jane'?]child1.names.push('Amy')console.log(child1.names)?//?[?'William',?'Jane',?'Amy'?]console.log(child2.names)?//?[?'William',?'Jane'?]child1.getNames()?//?[?'William',?'Jane',?'Amy'?]child2.getNames()?//?[?'William',?'Jane'?]

优点:

子类实例的属性是独有的

子类实例的方法都是继承父类原型上的方法,不用每次都创建

创建子类实例时可以向父类构造函数传参缺点:

要调用两次父类的构造函数,效率低。且在newFather()创建的对象上也有并不需要的属性。

function?Child()?{????Child.prototype?=?new?Father()8???//?第一次调用}Child.prototype?=?new?Father()??//?第二次调用四、原型式继承

当我们想要在一个对象的基础之上去构造另一个对象而不想创建额外的构造函数,就非常适合使用原型式继承。

Child.prototype?=?new?Father()2

上面的createObj()其实就是ES5中Object.create()的模拟实现,其中Object.create还可以接收第二个参数,它是一个对象,对象里可以定义要创建的对象独有的属性和方法。缺点:

和原型链继承一样,实例的引用类型的属性都是共享的。

五、寄生式继承

创建一个实现继承的函数,在函数里以某种方式增强对象。

Child.prototype?=?new?Father()3

缺点:

和借用构造函数的继承方式一样,每个实例都要重新创建同一方法。

六、寄生组合式继承

回顾一下前面使用的组合式继承

function?Father(name)?{????this.name?=?name}Father.prototype.getName?=?function?()?{????console.log(this.name)}function?Child(name,?age)?{????Father.call(this,?name)??this.age?=?age}Child.prototype?=?new?Father()

上面代码的最后一步是为了继承父类的方法,但是newFather()又被调用了一次且它得到的对象上面也有name属性,而我们并不需要这些属性,我们只是需要这个对像把实例对象与Father.prototype对象联系起来。

所以我们可以直接创建一个对象,让这个对象的构造函数的prototype指向Father.prototype

function?Father(name)?{????this.name?=?name}Father.prototype.getName?=?function?()?{????console.log(this.name)}function?Child(name,?age)?{????Father.call(this,?name)????this.age?=?age}//?Child.prototype?=?new?Father()function?F()?{}F.prototype?=?Father.prototypeChild.prototype?=?new?F()let?child1?=?new?Child('William',?19)console.log(child1.name)?//?Williamchild1.getName()?//?William

将上面的过程封装一下:

Child.prototype?=?new?Father()6

优点:

只调用了一次父类构造函数且避免了新增不必要的属性,效率更高。

寄生式组合继承可以算是引用类型继承的最佳模式。

总结?原型链继承:Child.prototype?=?new?Father()

优点:简单直观,子类的实例方法是共享的,不用每次都创建。

缺点:子类实例继承的引用属性都是共享的。

?借用构造函数:Child.prototype?=?new?Father()8

优点:子类继承的属性不共享

缺点:实例的方法每次都要重新创建。

?组合继承:Child.prototype?=?new?Father()8Child.prototype?=?new?Father()

优点:子类的实例方法是共享的,不用每次都创建。子类继承的属性独有不共享。

缺点:调用了两次父类构造函数,且newFather()得到的对象上的属性我们并不需要。

?原型式继承let?child1?=?new?Child()console.log(child1.name)????//?Willam0

优点:直接实现对象继承另一个对象而无需构造函数。

缺点:实例继承的属性都是共享的。

?寄生式继承

在原型式继承的基础上增强得到的对象。

缺点:和借用构造函数的继承方式一样,每个实例都要重新创建同一方法。

?寄生组合式继承

组合继承与寄生继承的结合

优点:只调用了一次父类构造函数且避免了新增不必要的属性,效率更高。

原文:https://juejin.cn/post/7094858433647771661

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
北京注册成立一个公司需要多少钱 北京公司都是什么 手机导航地图语音怎么下载 如何分别真金和仿金首饰 怎样区分真金和仿金首饰呢 小学生新年晚会主持人的串词!!(不要太多)急 大大后天就需要了!!!_百度... 周年晚会策划公司 奥格瑞玛传送门大厅在哪 奥格瑞玛传送门大厅怎么走 锻炼颈椎的几个动作 水多久能结冰 冰能在多长时间内形成 JavaScript继承的6种方式复盘 请问弹电子琴时双手合不起来怎么办啊? 网页打开了以后。图片都是红叉叉 有的显示不完全 怎么办 ...的图片有的可以打开,有的只显示红叉,视频都可以正常观看,怎么... 网页上的图片不能正常显示,是红叉,怎样解决? ...些图片显示的地方变成红叉,刷新后部分能显示,怎么让它保持不显示红... 为什么网页上有的图片显示不了,左上角是红叉 梦见同事家乡那边的混混打了什么寓意 新浪微博里有人转发了我的头像照片 请问 要是我把头像照片删除的话... ...上变成黑名单用户了 发的微博也删了 也不能换头像或者发微博... 唐太宗为什么被尊奉为天可汗100字 我想问问开生鲜超市需办什么证件 ...还想开一家超市可是我还想两家超市用一个名怎么办 为什么我手机微信微博空间里面的图片都显示不出了 微博的图片就是加载... ...图片很小。我放微博,显示的好小,但是为什么有些人的就好大好清晰... 电脑微信接收的文件中怎么办? 为什么微博有时显示照片,有时又不显示,如图 微博发的图片不知道是否违规,但同一张图片有的人手机就能看到,有的人就... 昨天在D盘中删除了一个文件结果图片下载失败? 丁桂儿脐贴能治胀气吗 ...细胞共性的描述正确的是( )A.都能进行有氧呼吸B.都有由DNA和蛋白质... ...成绩是120,左脑52,右脑68,是一个右脑型思维者,我想知道右脑型思维者... 在网上做了个智商测试,测智网,总分142,左脑73,右脑69,这个成绩怎么样... 测了一下智商,126左脑102,右脑24. 我的成绩在班里排第6年级23年级有60... 关于细胞呼吸作用的图。怎么看。A.B.C.D.E各个点代表什么?详解。 唐太宗被称为 唐太宗他被尊称为什么 ...霉菌、水绵的细胞都含有核糖体、DNA和RNAB.能 王奕丰著名部落风格肚皮舞者 at舞是什么意思? 父母喜欢让子女从事的工作有哪些? BELLE肚皮舞者 在玩诛仙2时 我本来在神域 后来到了天音寺 现在想再回到神域 怎么回去... 诛仙2 神族33级跑到天音了 然后怎么回神域 诛仙2中在天音寺怎么回到神域? 怎么从天音寺去神农殿`我怎么回不去了` 诛仙2烈山从新手村出来后怎么回去? 微信不能存照片到电脑(微信不能存照片到电脑怎么办) 我是30级多的小狐,去了天音就回不到烈山了。请大家告诉我如何回去? 去天音寺做任务的烈山怎么回去