不要再混淆啦!一文带你学会原型链继承、构造函数继承、寄生组合继承、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.设置…...
Django CORS Headers终极配置指南:Vue、React、Angular前端框架完美集成方案
Django CORS Headers终极配置指南:Vue、React、Angular前端框架完美集成方案 【免费下载链接】django-cors-headers Django app for handling the server headers required for Cross-Origin Resource Sharing (CORS) 项目地址: https://gitcode.com/gh_mirrors/d…...
保姆级教程:用YOLOv11+PyQt5打造你的专属天气识别桌面应用(附完整源码)
从零构建基于YOLOv11的智能天气识别桌面应用 窗外阴云密布,你是否曾好奇此刻的天气状况究竟如何?现代计算机视觉技术让机器也能像人类一样"看懂"天气。本文将带你完整实现一个能识别11种天气类型的桌面应用,从模型加载到界面交互&a…...
突破常规认知的编辑器革命:TinyEditor轻量级代码编辑器深度解析
突破常规认知的编辑器革命:TinyEditor轻量级代码编辑器深度解析 【免费下载链接】TinyEditor A functional HTML/CSS/JS editor in less than 400 bytes 项目地址: https://gitcode.com/gh_mirrors/ti/TinyEditor 当开发者在移动设备上调试代码,或…...
WindowsCleaner深度解析:如何用开源工具轻松解决C盘空间不足问题
WindowsCleaner深度解析:如何用开源工具轻松解决C盘空间不足问题 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服! 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner 你是否经常遇到C盘空间不足的困扰&am…...
金三银四这波我就先上车了兄弟们,大模型(LLMs)从基础到进阶:全面解析与实战指南
本文全面解析了大模型(LLMs)的基础、进阶和微调面,涵盖了主流开源模型体系、prefix LM与causal LM的区别、涌现能力的原因、大模型LLM架构、LLMs复读机问题及其缓解方法、不同模型的选择场景、专业领域模型需求、处理长文本的方法、全参数微调…...
低代码AI开发:这些工具让AI原生应用开发效率提升10倍
低代码AI开发:这些工具让AI原生应用开发效率提升10倍 关键词:低代码开发、AI原生应用、开发效率、AutoML、拖拽式建模、企业级AI落地、工具链整合 摘要:传统AI开发需要精通算法、数据处理和工程实现,门槛高且周期长。本文将揭秘“…...
XL6008直流升压电路设计与应用详解
1. 直流升压电路设计详解1.1 应用背景与需求分析便携式电子设备在现代生活中扮演着越来越重要的角色,从手持扫码设备到数码相机闪光灯,这些设备对电源系统提出了特殊要求。由于便携性限制,设备通常采用单节或少量电池供电,导致输出…...
效率提升50%:OpenClaw+GLM-4.7-Flash的会议纪要自动化
效率提升50%:OpenClawGLM-4.7-Flash的会议纪要自动化 1. 为什么需要自动化会议纪要 作为技术团队负责人,我每周要参加至少8场会议。过去两年里,我尝试过各种会议纪要工具——从讯飞听见的语音转写,到Notion AI的摘要生成&#x…...
远程收款好用服务商
在数字化支付日益普及的今天,远程收款成为许多商家和创业者的重要需求。然而,由于各种风控限制,微信支付、支付宝等主流支付平台在异地收款时常常出现异常提示或风险拦截,给用户带来了不少困扰。本文将对比分析几家提供远程收款服…...
FreeFileSync 14.9更新:多维度优化提升使用体验
FreeFileSync 14.9:核心功能更新亮点FreeFileSync作为一款适用于Windows、macOS和Linux的开源文件夹对比和同步软件,在14.9版本有了诸多重要更新。在Linux系统方面,支持高DPI显示器上的200%显示缩放,这对于使用高分辨率显示器的用…...
