不要再混淆啦!一文带你学会原型链继承、构造函数继承、寄生组合继承、ES6继承
JS继承目录
- 一、原型链继承
- 2、构造函数继承
- 3、组合继承
- 4、寄生组合继承
- 5、ES6继承
js有几种经典的继承方式。比如
原型链继承、
构造函数继承、
组合继承、
寄生组合继承、
ES6继承。让我们一一分析并实现。同时了解每种方案的优缺点。
其实js的继承本质上是通过原型链机制实现的扩展。不管是哪种继承方式,都是通过操作父类原型链和子类原型链形成关联关系实现的。只是不同实现中需要考虑不同的问题。在实际项目开发中,建议尽可能使用ES6的class extends实现。
其他实现方式主要是理解背后的原理和思想。
一、原型链继承
// 定义父类
function Parent() {this.name = "parent";
}// 在父类的原型上定义方法
Parent.prototype.getName = function() {return this.name;
}// 定义子类
function Child() {this.name = "child";
}// 子类继承父类,这里是关键,实现原型链继承
Child.prototype = new Parent();// 实例化子类
var child1 = new Child();console.log(child1.getName()); // 输出 "child"
缺陷:
原型链继承的一个主要问题是包含引用类型值的原型属性会被所有实例共享。 换而言之,如果一个实例改变了该属性,那么其他实例的该属性也会被改变
比如以下代码就会出问题:我明明只想修改了 child1 的arr,我不想修改 child2 的 arr。但是实际结果却是, child2 的 arr 也被修改了。
// 定义父类function Parent() {this.arr = [1, 2, 3];}// 定义子类function Child() {}// 子类继承父类,这里是关键,实现原型链继承Child.prototype = new Parent();// 实例化子类var child1 = new Child();var child2 = new Child();child1.arr.push(4);console.log(child1.arr); console.log(child2.arr);
上述问题能否避免呢?答案是可以的。我们可以通过构造函数继承来避免上述问题。
2、构造函数继承
构造函数继承,通过使用 call 或 apply 方法,我们可以在子类型构造函数中执行父类型构造函数,从而实现继承。比如以下例子,child1 和 child2 都继承了 Parent 的 sayHello() 方法。
这种继承方式的好处是,原型属性不会被共享(所以不会出现上述问题)。我们可以尝试console.log() child1 和 child2 的 sayHello方法, 通过 === 判断得知,他们并不是同一个方法。换而言之, 修改其中任何一个 sayHello ,不会影响另一个 sayHello。
// 父类function Parent() {this.sayHello = function () {console.log("Hello");};}Parent.prototype.a = "我是父类prototype上的属性";// 子类function Child() {Parent.call(this);}// 创建两个 Child 实例var child1 = new Child();var child2 = new Child();console.log(child1.sayHello === child2.sayHello); // 输出 false
缺陷:
那么这种方式的缺点是什么呢?它不能继承父类 prototype 上的属性。比如以下例子,父类的prototype 上有个 a 属性(它是一个字符串)。按理来说,child 类继承了 parent 类,因此 child 实例化的对象也得有 a 属性,但 console.log(child1.a) 之后可以看出,child1.a 是 undefined 。
为了解决以上缺点,我们引入了组合继承。
3、组合继承
组合继承可以理解为原型链继承 + 构造函数继承。 可以看以下例子:
// 父类function Parent() {this.sayHello = function () {console.log("Hello");};}Parent.prototype.a = "我是父类prototype上的属性"; // 子类function Child() {Parent.call(this);}Child.prototype = new Parent();var child1 = new Child();
实际上,只是在构造函数继承的基础上,加了一行代码:Child.prototype = new Parent();
这就解决了构造函数继承的缺点。 构造函数继承不能继承父类 prototype 上的属性。但是加上这行代码之后,每当我们在 child 对象上找不到属性,就会去 Child.prototype 上去寻找,而 Child.prototype 是一个 Parent 对象,如果 Parent 对象上还找不到,就会去 Parent.prototype 上去寻找。如此以来,组合继承就能继承父类 prototype 上的属性。
但是,组合继承有没有缺点呢?它也有缺点:调用了 2 次 Parent()。 它在 child 的 prototype 上添加了父类的属性和方法。
很多读者可能读不懂上面这句话,我们通过一个具体的例子来说明。child1 是一个子类对象,这个对象本身有 sayHello 方法。而 Child.prototype 上也有一个 sayHello 方法。
为什么child1本身有 sayHello 方法?因为在 Child() 构造函数内部,我们调用了一次 Parent(), 这就给 child1对象添加了 sayHello 方法。
而为什么 Child.prototype 上也有一个 sayHello 方法?因为 Child.prototype = new Parent();
细心的你·可能发现了,我们每次调用 child1.sayHello() 的时候,永远是调用的child1本身有 sayHello 方法。换句话说,Child.prototype 上 sayHello 方法我们根本不需要。
那么有没有办法解决这个问题呢?于是我们引入了寄生组合继承。
4、寄生组合继承
// 父类function Parent() {this.sayHello = function () {console.log("Hello");};}Parent.prototype.a = "我是父类prototype上的属性";// 子类function Child() {Parent.call(this);}// 创建一个没有实例方法的父类实例作为子类的原型Child.prototype = Object.create(Parent.prototype);// 修复构造函数的指向Child.prototype.constructor = Child;var child1 = new Child();
我们发现,只需要在组合继承的基础上,把 Child.prototype = new Parent(); 替换为 Child.prototype = Object.create(Parent.prototype);
这两者有什么区别呢? Object.create(Parent.prototype) 创造了一个空对象,这个空对象的__proto__ 是 Parent.prototype 。所以它继承了 Parent 原型链上的属性和方法。由于我们删除了Child.prototype = new Parent(); 我们不再调用 Parent() 构造函数,因此 Child.prototype 不再包含 Parent 的属性和方法。所以第三小节部分提到的问题就解决了,Child.prototype 上不再有 sayHello 方法。
那么寄生组合继承有没有缺点呢?当然也有缺点。看以下例子:
// 父类function Parent() {this.sayHello = function () {console.log("Hello");};}Parent.prototype.a = "我是父类prototype上的属性";// 子类function Child() {Parent.call(this);}Child.prototype.childFunction = () => {console.log("我是child方法");};// 创建一个没有实例方法的父类实例作为子类的原型Child.prototype = Object.create(Parent.prototype);// 修复构造函数的指向Child.prototype.constructor = Child;var child1 = new Child();child1.childFunction();
我们在 Child.prototype 上添加了 childFunction 。所有通过Child() 创建的实例对象,都应该有该childFunction() 方法。但是当我们调用 child1.childFunction() 的时候却报错,这是为什么呢?因为 Child.prototype = Object.create(Parent.prototype); 这一行代码使 Child.prototype 指向了一个新对象, 原来对象上的属性和方法都会丢失。
所以寄生组合继承的缺点就是,Child.prototype 的原始属性和方法会丢失。
5、ES6继承
ES6提供了class语法糖,同时提供了extends用于实现类的继承。这也是项目开发中推荐使用的方式。
使用class继承很简单,也很直观:
class Parent {constructor() {this.name = 'zzz'}getName() {return this.name}
}class Child extends Parent {constructor() {// 这里很重要,如果在this.topic = 'z'后面调用,会导致this为undefined,具体原因可以详细了解ES6的class相关内容,这里不展开说明super()this.topic = 'z'}
}const child = new Child()
child.getName() // zzz
想通过图文教程学习可看:JS继承
相关文章:
不要再混淆啦!一文带你学会原型链继承、构造函数继承、寄生组合继承、ES6继承
JS继承目录 一、原型链继承2、构造函数继承3、组合继承4、寄生组合继承5、ES6继承 js有几种经典的继承方式。比如 原型链继承、 构造函数继承、 组合继承、 寄生组合继承、 ES6继承。让我们一一分析并实现。同时了解每种方案的优缺点。 其实js的继承本质上是通过原型链机制…...
828华为云征文|华为云Flexus X实例Windows Server 2019安装护卫神防火墙——为企业运维安全发挥重要作用!!!
前言 公司最近需要选购一台华为云Windows服务器部署产品应用,但是考虑到Windows的安全性至关重要。护卫神防火墙无疑是守护Windows系统安全的得力助手。 华为云以其强大的性能和稳定的服务,为众多企业和开发者提供了可靠的云端基础设施。在网络环境日益复…...
最新的iOS 18版本和Android 15版本系统分别升级了哪些功能?
iOS 18 推出了多项激动人心的新功能和改进。以下是一些亮点: 日记应用:一款全新的日记应用,旨在帮助用户记录日常经历、想法和活动,利用设备内置智能功能建议主题,并根据照片、位置和其他数据组织条目。 眼动追踪导航…...
window系统DockerDesktop 部署windows容器
目录 参考文献1、安装Docker Desktop1.1 下载安装包1.2 安装教程1.3 异常解决 2、安装windows容器2.1 先启动DockerDesktop 软件界面2.2 检查docker版本2.3 拉取windows镜像2.4 网盘下载windows镜像 参考文献 windows容器docker中文官网 Docker: windows下跑windows镜像 1、安…...
CSDN文章导出md并迁移至博客园
一、获取所有文章地址 1.进csdn首页,点击自己的头像 2.在个人主页界面,按F12打开控制台,并找到network,找到get-business开头的请求,右键copy他的url 3.选择console,输入一下代码,其中fetch里面的url是你刚…...
计算机组成原理(笔记5原码和补码的乘法以及直接补码阵列乘法器 )
原码一位乘法 手算:过程 令x′|x|0.x1x2…xn-1xn,y′|y|0.y1y2…yn-1yn 同时令乘积P′ |P| x′ y′,有: x′ y′ x′(0.y1y2…yn-1yn) x′ (y12-1y22-2…yn-12-(n-1)yn2-n) 2-1(y1x′2-1(y2x′…2-1(yn-1x′2-1(ynx′0))…))…...
【hot100-java】【括号生成】
R9-回溯篇 枚举填左括号 class Solution {private int n;private char[] path;private final List<String> retnew ArrayList<>();public List<String> generateParenthesis(int n) {this.nn;//所有括号长度都是n*2pathnew char [n*2];dfs(0,0);return ret;…...
k8s_资源管理介绍
资源管理介绍 在k8s中,所有内容都抽象成资源,用户需要通过操作资源来管理k8s k8s本身就是一个集群系统,用户可以在集群中部署服务,在k8s集群中运行一个个的容器,将指定的程序部署到容器中 k8s最小的管理单元是pod&…...
操作简单 地检编码器 武汉正向科技售后优质
武汉正向科技的地检编码器以导轨式安装方式,方便拆卸,立体结构造型,节约空间。 格雷母线定位系统由格雷母线,天线箱,解码器,编码器等部件构成。 用途 地上检测方式地址信号的编码、功率放大,与…...
2024中国新能源汽车零部件交易会,开源网安展示了什么?
近日,2024中国新能源汽车零部件交易会在十堰国际会展中心举行。开源网安车联网安全实验室携车联网安全相关产品及解决方案亮相本次交易会,保障智能网联汽车“车、路、云、网、图、边”安全,推动智能网联汽车技术突破与产业化发展。 中国新能源…...
Java解析嵌套jar中class文件
一、简述 Maven项目通过package打成jar包后,jar包中包含所有依赖lib文件。本文介绍了两种方式解析嵌套jar中的class文件,一种是通过spring-boot-loader包JarFileArchive,另一种是util包中JarFile。 二、JarFileArchive方式 1.spring-boot-…...
【含文档】基于Springboot+Vue的高校竞赛管理系统(含源码+数据库+lw)
1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 系统定义了三个…...
在大模型应用层面区分对比检索增强生成RAG技术和知识库技术
在前文: 《RAG(Retrieval-Augmented Generation)检索增强生成技术基础了解学习与实践》 初步了解实践了RAG技术,后面好多朋友也在沟通聊到了前面大模型另一项技术就是本地知识库方法,之前基于LangChain本地知识库的方式,可以本地…...
云和恩墨携手华为,发布zCloud数据库备份管理一体机并宣布共建数据保护生态...
为期三天的第九届华为全联接大会(HUAWEI CONNECT 2024)于9月19日在上海世博中心&展览馆盛大召开。20日下午,一场围绕“全场景数据保护,护航数智化时代”的专题论坛举办,云和恩墨受邀参加,并期待与华为合…...
Linux系统备份Gitee等云git所有仓库与所有分支的数字资产
思路: 1. ssh 配置 2. reps.txt 列出所有仓库名 3. exp的自动化备份脚本 -- 环境安装: exp需要依赖安装的文件,所以先执行下(以ubuntu为例): sudo apt-get install expect 操作步骤: ssh 配置 1. 添加公钥至 …...
JavaScript 条件循环语句
条件循环语句是编程中的一种控制结构,它允许程序根据特定条件重复执行一段代码,直到满足某个条件为止。这种结构通常包括条件语句和循环语句,它们共同作用,使得程序能够根据预设的条件来决定是否继续执行循环体中的代码。 fo…...
LeetCode2207解题思路
题目描述 字符串中最多数目的子序列 解题思路: 题目要求我们找到在 text 中 找到最多可组成 pattern 的字符串个数,并且允许在 text 的任意位置插入 pattern 中一个字符,也就是说我们只需要考虑 text 中的 pattern 含有的字符即可。例如示例…...
opencv图像增强十四:opencv两种白平衡介绍及实现
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、白平衡介绍二、灰度世界法三、完美反射法 前言 在摄影与影像领域,白平衡是一个至关重要的概念。它直接影响着画面的色彩表现,关系到…...
Linux标准IO(四)-格式化I/O输入
C 库函数提供了 3 个格式化输入函数,包括:scanf()、fscanf()、sscanf(),其函数定义如下所示: #include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *stream, const char *format, ...); int sscanf(c…...
分布式安装LNMP
目录 搭建LNMP架构 安装mysql 1.上传mysql软件包,关闭防火墙和核心防护 2.安装环境依赖包,桌面安装可能有自带的数据库除 3.配置软件模块 4.编译及安装 5.创建mysql用户 6.修改mysql 配置文件 7.更改mysql安装目录和配置文件的属主属组 8.设置…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
