js常见的七种继承及实现
在 JavaScript 中,常见的继承方式有以下七种:
大厂面试题分享 面试题库
前后端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
1. 原型链继承
原型链继承是 JavaScript 中一种基于原型的继承方式,它通过将一个构造函数的实例作为另一个构造函数的原型,从而实现继承。具体来说,就是在子类的构造函数中通过 Child.prototype = new Parent() 的方式来继承父类的属性和方法。
以下是一个实现原型链继承的示例代码:
// 定义父类构造函数functionParent(name) {this.name = name;
}// 父类原型上的方法Parent.prototype.sayName = function() {console.log('My name is ' + this.name);
}// 定义子类构造函数functionChild(name, age) {this.age = age;
}// 将子类的原型设置为父类的实例Child.prototype = newParent();// 子类原型上的方法Child.prototype.sayAge = function() {console.log('I am ' + this.age + ' years old');
}// 创建子类实例var child = newChild('Tom', 10);// 调用子类实例的方法
child.sayName(); // My name is Tom
child.sayAge(); // I am 10 years old复制代码
在上面的示例代码中,我们首先定义了一个父类构造函数 Parent,并在其原型上定义了一个方法 sayName。然后我们定义了一个子类构造函数 Child,并通过将子类的原型设置为父类的实例来实现继承。最后我们创建了一个子类实例 child,并调用其方法来验证继承是否成功。
优点:
简单易懂:原型链继承是一种简单的继承方式,易于理解和实现。
父类方法更新会自动同步到子类实例:由于子类实例的原型指向父类实例,所以父类的方法更新会自动同步到子类实例中。
可以重用父类方法:由于子类实例可以访问父类的原型,因此可以重用父类的方法,从而减少代码量。
缺点:
所有子类实例共享原型对象:由于所有子类实例的原型都指向同一个对象,因此一个实例对原型对象的修改会影响到其他实例。
无法向父类构造函数传递参数:原型链继承无法向父类构造函数传递参数,因此子类实例无法向父类构造函数传递参数,也无法对父类实例进行初始化。
无法实现多继承:由于JavaScript中一个对象只能有一个原型对象,因此原型链继承无法实现多继承。
2. 借用构造函数继承
JavaScript中的借用构造函数继承是一种通过调用父类构造函数来实现继承的方式。这种继承方式有以下特点:
子类实例拥有了父类构造函数中定义的属性和方法。
子类实例与父类实例之间不存在原型链的关系,因此可以避免共享原型对象带来的问题。
子类无法重用父类原型对象上的方法。
以下是一个借用构造函数继承的示例代码:
functionAnimal(name) {this.name = name;
}Animal.prototype.sayName = function() {console.log('My name is ' + this.name);
}functionDog(name, age) {Animal.call(this, name); // 借用Animal构造函数,并将this指向Dog实例this.age = age;
}let dog1 = newDog('旺财', 2);
let dog2 = newDog('小白', 1);console.log(dog1.name); // '旺财'console.log(dog2.age); // 1
dog1.sayName(); // TypeError: dog1.sayName is not a function复制代码
在上面的代码中,Dog类通过调用Animal构造函数来实现继承,从而拥有了Animal类中定义的属性和方法。由于子类实例与父类实例之间不存在原型链的关系,因此修改一个实例的属性不会影响到其他实例。
但是需要注意的是,由于子类实例无法访问父类原型对象上的方法,因此在上面的代码中,dog1实例调用sayName()方法会报错。如果需要在子类中重用父类原型对象上的方法,可以考虑使用组合继承或寄生组合式继承。
优点:
可以向父类构造函数传递参数:借用构造函数继承可以向父类构造函数传递参数,从而可以对父类实例进行初始化。
避免了原型对象共享的问题:由于借用构造函数继承创建了一个新的对象,因此避免了原型对象共享的问题。
可以实现多继承:由于JavaScript中可以在一个构造函数中调用多个构造函数,因此可以通过借用构造函数继承来实现多继承。
缺点:
无法重用父类方法:由于借用构造函数继承创建了一个新的对象,因此无法重用父类的方法。
父类方法更新不会自动同步到子类实例:由于借用构造函数继承创建了一个新的对象,因此父类的方法更新不会自动同步到子类实例中。
无法访问父类原型上的属性和方法:由于借用构造函数继承只继承了父类的实例属性和方法,因此无法访问父类原型上的属性和方法。如果需要访问父类原型上的属性和方法,仍然需要通过将子类的原型指向父类的实例来实现。
3. 组合继承
JavaScript中的组合继承是一种结合借用构造函数和原型链继承的方式,它的核心思想是使用借用构造函数继承实例属性和方法,使用原型链继承共享属性和方法。
以下是一个组合继承的示例代码:
functionAnimal(name) {this.name = name;
}Animal.prototype.sayName = function() {console.log('My name is ' + this.name);
}functionDog(name, age) {Animal.call(this, name); // 借用Animal构造函数,并将this指向Dog实例this.age = age;
}Dog.prototype = newAnimal(); // 原型链继承Animal类的属性和方法Dog.prototype.constructor = Dog; // 修复构造函数指向let dog1 = newDog('旺财', 2);
let dog2 = newDog('小白', 1);console.log(dog1.name); // '旺财'console.log(dog2.age); // 1
dog1.sayName(); // 'My name is 旺财'复制代码
在上面的代码中,Dog类通过借用Animal构造函数继承实例属性和方法,通过原型链继承Animal类的属性和方法。由于子类实例与父类实例之间不存在原型链的关系,因此修改一个实例的属性不会影响到其他实例。同时,子类实例可以重用父类原型对象上的方法。
需要注意的是,由于在上面的代码中通过Dog.prototype = new Animal()创建了一个新的Animal实例,因此在创建Dog类时会调用两次Animal构造函数,造成了性能上的浪费。可以使用寄生组合式继承来解决这个问题。
具体来说,组合继承通过将父类的构造函数借用到子类中,从而实现了父类属性的继承,同时通过将子类的原型设置为一个新的父类实例,从而实现了父类方法的继承。这种继承方式具有以下优缺点:
优点:
父类的构造函数可以传递参数,并且不会影响到其他实例。
子类实例可以访问父类原型对象上的方法,可以重用父类的方法。
可以实现多继承。
实现简单、易于理解。
缺点:
子类实例会同时拥有自己的属性和方法,以及父类的属性和方法,可能导致内存浪费和属性名冲突的问题。
在创建子类实例时,父类构造函数会被调用两次,可能会影响性能。
4. 原型式继承
JavaScript中的原型式继承是一种基于已有对象创建新对象的继承方式,它利用了对象的动态特性,通过封装一个函数来实现继承。该函数接收一个用作新对象原型的对象作为参数,并返回一个新对象,从而实现了继承。该方式与借用构造函数继承类似,但它并不涉及到构造函数和实例的概念。原型式继承具有以下特点:
基于已有对象创建新对象。
可以使用Object.create()方法实现。
可以将一个对象作为另一个对象的原型对象。
可以使用原型对象的属性和方法,但不会影响到原型对象本身。
下面是一个使用原型式继承的示例代码:
let animal = {type: 'animal',sayType: function() {console.log('I am a ' + this.type);}
};let dog = Object.create(animal); // 使用animal对象作为dog对象的原型
dog.type = 'dog';dog.sayType(); // 'I am a dog'复制代码
在上面的代码中,animal对象拥有一个type属性和一个sayType方法,dog对象通过使用animal对象作为原型对象来实现了继承。因此,dog对象可以使用原型对象的属性和方法,但并不会影响到原型对象本身。此外,通过给dog对象添加一个type属性,也可以覆盖原型对象的type属性,实现对父对象属性的重写。原型式继承的优点在于可以方便地实现对象的复用,但也容易导致对象之间的耦合,不易于维护。
具体来说,它通过创建一个空对象,并将其原型设置为一个已有对象,然后向这个空对象中添加属性和方法来实现继承。原型式继承具有以下优缺点:
优点:
简单、易于理解和实现。
可以基于一个对象创建多个对象,实现对象复用。
缺点:
父对象的引用属性会被所有子对象共享,因此子对象的修改会影响到其他子对象。
子对象无法像传统的类继承一样判断自己是否是父对象的实例。
无法实现多继承。
5. 寄生式继承
JavaScript中的寄生式继承是一种基于已有对象创建新对象的继承方式,类似于原型式继承。它的主要区别是,在新创建的对象上增加一个方法,而这个方法的作用是以某种方式增强对象,然后返回这个对象。这种继承方式得名于“寄生”,因为增强对象的方法通常是基于已有的对象进行“寄生”而得名。
寄生式继承的优点是可以封装继承过程,并且可以向对象中添加一些额外的属性和方法。但是和原型式继承一样,也存在父对象的引用属性被所有子对象共享、无法判断实例是否是父对象的实例等问题。
以下是一个使用寄生式继承的示例代码:
functioncreateAnimal(type) {let animal = {type: type,sayType: function() {console.log('I am a ' + this.type);}};// 基于animal对象进行寄生增强let dog = Object.create(animal);dog.bark = function() {console.log('woof woof');};return dog;
}let myDog = createAnimal('canine');
myDog.sayType(); // 'I am a canine'
myDog.bark(); // 'woof woof'复制代码
在上面的代码中,我们定义了一个名为createAnimal的函数,用于创建一个继承自animal对象的新对象。我们在这个新对象上增加了一个bark方法,用于让对象发出叫声。最后,我们返回这个新对象,并将它赋值给myDog变量。通过这样的方式,我们成功地实现了寄生式继承。
具体来说,它在原型式继承的基础上增加了一个包装函数,该函数用于封装继承过程中的一些增强行为。寄生式继承具有以下优缺点:
优点:
简单、易于理解和实现。
可以基于一个对象创建多个对象,实现对象复用。
可以在不修改原对象的情况下,对继承过程进行一些增强,例如添加新的属性和方法。
缺点:
父对象的引用属性会被所有子对象共享,因此子对象的修改会影响到其他子对象。
子对象无法像传统的类继承一样判断自己是否是父对象的实例。
增强行为可能会带来一定的性能开销。
可能会导致代码的可读性降低。
6. 寄生式组合继承
JavaScript中的寄生式组合继承是一种结合了组合继承和寄生式继承的继承方式。具体来说,它在组合继承的基础上,通过寄生式继承来解决组合继承中重复调用父构造函数的问题。
下面是一个使用寄生式组合继承的示例代码:
functionAnimal(name) {this.name = name;this.type = 'mammal';
}Animal.prototype.sayName = function() {console.log('My name is ' + this.name);
};functionDog(name, breed) {Animal.call(this, name);this.breed = breed;
}// 使用寄生式继承继承Animal.prototypeDog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;Dog.prototype.sayBreed = function() {console.log('I am a ' + this.breed);
};let myDog = newDog('Max', 'Golden Retriever');
myDog.sayName(); // 'My name is Max'
myDog.sayBreed(); // 'I am a Golden Retriever'复制代码
在上面的代码中,我们定义了Animal和Dog两个构造函数,其中Animal构造函数定义了一个name属性和一个sayName()方法,Dog构造函数在Animal的基础上添加了一个breed属性和一个sayBreed()方法。为了实现寄生式组合继承,我们使用Object.create()方法基于Animal.prototype创建了一个新的对象,并将其赋值给Dog.prototype,从而使得Dog.prototype的原型链指向了Animal.prototype。同时,我们还将Dog.prototype的constructor属性设置为Dog,以保证继承链的完整性。最后,我们通过调用Animal构造函数并将this指向Dog对象,实现了对Animal属性的继承。通过这种方式,我们既避免了组合继承中重复调用父构造函数的问题,又保留了寄生式继承的灵活性,实现了一个高效而且灵活的继承方式。
优点:
实现了属性和方法的完整继承。
避免了组合继承中重复调用父类构造函数的问题,提高了性能。
可以在不修改原对象的情况下,对继承过程进行一些增强,例如添加新的属性和方法。
缺点:
增加了一层包装函数,可能会带来一定的性能开销。
可能会导致代码的可读性降低。
7. class继承
在ES6及以上的版本中,JavaScript引入了class关键字,用于定义类,从而实现面向对象编程。class继承是一种通过类来实现继承的方式,它使用extends关键字来指定父类,并通过super关键字来调用父类的构造函数和方法。
以下是一个使用class继承的示例代码:
classAnimal {constructor(type) {this.type = type;}sayType() {console.log('I am a ' + this.type);}
}classDogextendsAnimal {constructor(type, name) {super(type);this.name = name;}sayName() {console.log('My name is ' + this.name);}
}let dog = newDog('canine', 'Fido'); // 创建一个新的Dog对象dog.sayType(); // 'I am a canine'
dog.sayName(); // 'My name is Fido'复制代码
在上面的代码中,我们首先定义了一个Animal类,它包含一个构造函数和一个sayType()方法。然后我们通过extends关键字来指定Dog类的父类为Animal,并在Dog类的构造函数中通过super关键字来调用Animal构造函数,并实现了sayName()方法。最后,我们创建一个新的Dog对象,并调用它的方法来测试继承是否成功。
优点:
代码可读性高,更易于理解和维护。
语法简洁,可以更快地编写代码。
可以使用现代JavaScript特性,如箭头函数、解构赋值等。
缺点:
与ES5及以下版本的JavaScript不兼容。
需要编译才能运行在低版本浏览器中。
某些开发者可能认为使用类和继承违背了JavaScript的本质。
总体来说,class继承是一种非常方便的继承方式,特别是在面向对象编程中,能够大大简化代码的编写和维护。但在一些特定情况下,其他继承方式可能更为适合。
大厂面试题分享 面试题库
前后端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
相关文章:

js常见的七种继承及实现
在 JavaScript 中,常见的继承方式有以下七种:大厂面试题分享 面试题库前后端面试题库 (面试必备) 推荐:★★★★★地址:前端面试题库1. 原型链继承原型链继承是 JavaScript 中一种基于原型的继承方式&#…...

案例分析之——理由Mybatis动态SQL实现复用
无复用思想的做法: 在没有复用思想的时候,就只顾着实现功能。比如开发过程中涉及到两个表的更新功能,每需要更新一处,就写一个接口,结果出现了写了11个接口的情况。 这样虽然功能实现了,可是可能自…...

MCM 箱模型建模方法及大气 O3 来源解析实用干货
OBM 箱模型可用于模拟光化学污染的发生、演变过程,研究臭氧的生成机制和进行敏感性分析,探讨前体物的排放对光化学污染的影响。箱模型通常由化学机理、物理过程、初始条件、输入和输出模块构成,化学机理是其核心部分。MCM (Master Chemical M…...

【独家】华为OD机试 - 最长连续交替方波信号(C 语言解题)
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...

代码随想录算法训练营第二十一天打卡 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先
打卡第21天,继续二叉树,前几天终于补完了,感觉难度上来了。 今日任务 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数 二叉树的最近公共祖先 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root ,返回 树中任意两不…...

免费下载丨一看即会,Serverless 技术进阶必读百宝书
过去一年,全球正在加速推进云计算的 Serverless 化进程。Serverless 架构已经逐渐从“被接受”走向了“被学习”和“被应用”。云的产品体系正在 Serverless 化,从计算、存储、数据库到中间件,越来越多的云产品采用了 Serverless 模式。服务器…...

SQL语句的加锁方式 - Mysql 锁机制
SQL语句的加锁方式 - Mysql锁机制 SELECT ... FROM SELECT ... FOR UPDATE / SELECT ... FOR SHARED MODE SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE UPDATE ... WHERE ... DELETE FROM ... WHERE ... INSERT INSERT ... ON DUPLICATE KEY UPDATE REPLACE Mysql锁机…...

C#开发的OpenRA的游戏主界面怎么样创建4
继续游戏主界面创建的主题, 前面已经说到怎么样找到mainmenu.yaml来显示主界面,也说了怎么样找到各个子控件类。 现在就来仔细分析一下,主界面每一部分的功能。 比如下面这个区域的界面是怎么样创建: 要创建这一小部分的界面显示,也是需要做很多的工作。 因为在这里所有UI…...

覆盖5大主流开发平台的报表控件,它值得你一看
为什么大家现在都在使用第三方报表工具呢? 第三方报表工具是数据库存储,数据库程序通常可以存放的数据量是相当大的,可以处理非常复杂的数据结构关系,报表数据交互速度也非常快。不仅能够提高开发效率,还能实现灵活美…...

【冲刺蓝桥杯的最后30天】day4
大家好😃,我是想要慢慢变得优秀的向阳🌞同学👨💻,断更了整整一年,又开始恢复CSDN更新,从今天开始更新备战蓝桥30天系列,一共30天,如果对你有帮助或者正在备…...

spring boot actuator 动态修改日志级别
1 日志级别 Spring Boot Actuator包括在运行时查看和配置应用程序日志级别的功能。您可以查看整个列表,也可以查看单个记录器的配置,该配置由显式配置的日志级别和日志框架给出的有效日志级别组成。这些级别可以是: TRACEDEBUGINFOWARNERRORFATALOFFnu…...

兴达易控Modbus转Profinet网关连接1200Profinet转modbus接三菱A800变频器案例
下面介绍A800 变频器通过兴达易控modbus转profinet网关,使1200plc无需编程实现Profinet转modbus协议转换,把modbus变频器轻松组网 网络拓扑如下图 打开博图组态加载GSD文件,modbus转profinet网关从站接口接入到1200PLC上 配置modbus转profine…...

「SAP ABAP」OPEN SQL(四)【FROM语句】
💂作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后…...

一文吃透 SpringMVC 中的转发和重定向
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

Hbase操作命令
目录 创建表,表中有两个列族 baseinfo, schoolinfo 查看指定表全名空间中的表 查看表描述 禁用/启用 查看是否启用/禁用 删除表 注意,首先要将删除的表设置为禁用状态才可以删除,否则会报错 新增列族 删除列族 更改列族存储版本的限制 增…...

1>LINK : fatal error LNK1104: cannot open file ‘libconvtname.obj‘
我自己最后找到问题原因是: 引用的库名称没有.lib,只有libconvtname。 改成完整的libconvtname.lib即可。 以下是chatGPT的回答 The error message "fatal error LNK1104: cannot open file libconvtname.obj" usually occurs when Visual S…...

数据结构——链表OJ题目讲解(1)
作者:几冬雪来 时间:2023年3月7日 内容:数据结构链表OJ题目讲解 题目来源:力扣和牛客 目录 前言: 刷题: 1.移出链表元素: 2.链表的中间结点: 3. 链表中倒数第k个结点࿱…...

LeetCode_二分搜索_困难_154.寻找旋转排序数组中的最小值 II
目录1.题目2.思路3.代码实现(Java)1.题目 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums [0,1,4,4,5,6,7] 在变化后可能得到: 若旋转 4…...

面向对象设计模式:创建型模式之建造者模式
一、引入 Build:建造和构建具有建筑结构的大型物体 建楼:打牢地基、搭建框架、然后自下而上一层层盖起来。构建物体:通常需要先建造组成这个物体的各个部分,然后分阶段把它们组装起来 二、建造者模式 2.1 Intent 意图 Separate…...

集成学习boosting、bagging、stacking
目录 一、介绍 二、三种架构学习 (1)boosting (2)bagging (3)stacking 一、介绍: 对于单个模型来说很难拟合复杂的数,模型的抗干扰能力较低,所以我们希望可以集成多…...

数据模型(上):模型分类和模型组成
1.模型分类 数据模型是一种由符号、文本组成的集合,用以准确表达信息景观,达到有效交流、沟通的目的。数据建模者要求能与来自不同部门,具有不同技术背景,不同业务经验,不同技术水平的人员交流、沟通。数据建模者要了解每个人员的观点,并通过反馈证明理解无误,最终作…...

郑州轻工业大学2022-2023(2) 数据结构题目集 - ZZULI
6-1 线性表元素的区间删除 6-1 线性表元素的区间删除 分数 20 全屏浏览题目 切换布局 作者 DS课程组 单位 浙江大学 给定一个顺序存储的线性表,请设计一个函数删除所有值大于min而且小于max的元素。删除后表中剩余元素保持顺序存储,并且相对位置不能改变…...

【Python语言基础】——Python MySQL Drop Table
Python语言基础——Python MySQL Drop Table 文章目录Python语言基础——Python MySQL Drop Table一、Python MySQL Drop Table一、Python MySQL Drop Table 删除表 您可以使用 “DROP TABLE” 语句来删除已有的表: 实例 删除 “customers” 表: import…...

2023美团面试真题
面试前需要准备: 1. Java 八股文:了解常考的题型和回答思路; 2. 算法:刷 100-200 道题,记住刷题最重要的是要理解其思想,不要死记硬背,碰上原题很难,但 大多数的解题思路是相通的…...

【微信小程序开发全流程】篇章0:基于JavaScript开发的校园综合类微信小程序的概览
基于JavaScript开发的校园综合类微信小程序的概览 本文仅供学习,未经同意请勿转载 一些说明:上述项目来源于笔者我本科大三阶段2019年电子设计课程项目,在这个项目中,我主要是负责的部分有前端,前后端的对接…...

如何分析sql性能
1、前言 提到sql性能分析,可能都会想到explain,它在mysql里被称为执行计划,也就是说可以通过该命令看出mysql在通过优化器分析之后如何执行sql。mysql的内置优化器十分强大,它能帮我们把sql再次优化,以最低的成本去执…...

市场营销书籍推荐:《经理人参阅:市场营销》
要学好市场营销有什么好方法?答案是看书!比起碎片化地去阅读一些文章或看一些相关视频,读书来得更实在些。倘若能静下心来好好读上一本系统性的市场营销书籍,学好营销管理将不会再是一件难事。然而,问题的关键是&#…...

WPF 控件专题 MediaElement控件详解
1、MediaElement 介绍 MediaElement:表示包含音频和/或视频的控件。 MediaOpened在引发事件之前,ActualWidth控件将ActualHeight报告为零,因为媒体内容用于确定控件的最终大小和位置。 对于仅音频内容,这些属性始终为零。 对于固…...

基于SpringBoot+SpringCloud+Vue前后端分离项目实战 --开篇
本文目录前言做项目的三大好处强强联手(天狗组合)专栏作者简介专栏的优势后端规划1. SpringBoot 和 SpringCloud 的选择2. Mybatis 和 MybatisPlus 和 JPA 的选择3. MySQL 和 Mongodb 的选择4. Redis 和 RocketMQ5. 后端规划小总结后端大纲提前掌握的知识点一期SpringBoot二期S…...

循环队列的实现
我们知道队列的实现可以用单链表和数组,但是循环链表也可以使用这两种方式。首先我们来看看单链表:首先使用单链表,我们需要考虑循环队列的一些特点。单链表实现循环队列我们要考虑几个核心问题:首先我们要区别 解决 空 和 满 的问…...