关于JS继承的七种方式和理解
1.原型链继承
function Fun1() {this.name = 'parent'this.play = [1, 2, 3]
}
function Fun2() {this.type = 'child'
}Fun2.prototype = new Fun1()let s1 = new Fun2()
let s2 = new Fun2()
s1.play.push(4)
console.log(s1.play, s2.play) // [1, 2, 3, 4] [1, 2, 3, 4]
可以看到两个子类实例的属性都被影响了,这是因为通过原型链继承的子类实例的属性是共享的
原型链继承的弊端
-
共享引用类型属性
-
函数无法传递参数
-
原型链过长导致性能问题
-
难以维护和调试
2.构造函数继承
function Fun1() {this.name = 'parent'
}Fun1.prototype.getName = function () {return this.name
}function Fun2() {Fun1.call(this)this.type = 'child'
}let child1 = new Fun2()
let child2 = new Fun2()
child2.type = 'xxxx'console.log(child1, child2) // Fun2 { type: 'child', name: 'parent' } Fun2 { type: 'xxxx', name: 'parent' }console.log(child1.getName()) // child1.getName is not a function
可以看到构造函数继承确实不会共享属性了,但是构造函数继承只继承了父类的实例属性和方法,而不会继承父类原型上的方法
构造函数继承的弊端
-
无法复用父类原型上的方法
-
每个子类实例都会创建新的父类实例(每次创建子类实例时,都会调用一次父类构造函数,这会导致不必要的资源消耗,尤其是在父类构造函数中有复杂初始化逻辑或大量计算时。)
-
无法传递参数给父类构造函数(如果父类构造函数需要参数,子类构造函数必须显式地传递这些参数,否则父类构造函数中的初始化逻辑可能无法正常工作。)
-
难以维护和调试(构造函数继承的代码结构相对复杂,特别是在多层继承的情况下,可能会导致代码难以理解和维护。此外,调试时也更难追踪继承链中的问题。)
3.组合继承(伪经典继承)
function Fun1(name) {this.name = namethis.play = [1, 2, 3]
}Fun1.prototype.getName = function () {return this.name
}function Fun2() {Fun1.call(this, 'haha')this.type = 'child'
}Fun2.prototype = new Fun1()// 修复构造函数指针,确保子类的 constructor 属性正确指向其自身的构造函数
// 如果不写这段代码,下面的 child1 和 child2 的 constructor 就会指向 Fun1
Fun2.prototype.constructor = Fun2 let child1 = new Fun2()
let child2 = new Fun2()
console.log(child1.constructor === Fun2) // true
console.log(child2.constructor === Fun1) // falsechild1.play.push(4)
console.log(child1.play, child2.play) // [1, 2, 3, 4] [1, 2, 3]console.log(child1.getName(), child2.getName()) // haha haha
组合继承的优点
- 避免了原型链继承的共享属性问题(每个子类实例都有自己的属性副本,不会共享父类实例中的引用类型属性。)
- 可以继承父类原型上的方法(子类不仅继承了父类的实例属性,还可以继承父类原型上的方法,避免了重复定义方法的问题。)
- 灵活性高(可以在子类构造函数中调用父类构造函数时传递参数,确保父类初始化逻辑正常工作。)
组合继承的缺点
- 调用两次父类构造函数(组合继承的主要问题是它会调用两次父类构造函数:一次是在创建子类原型时 (
new Fun1()),另一次是在子类构造函数中 (Fun1.call(this, 'haha'))。这会导致不必要的性能开销,尤其是在父类构造函数中有复杂初始化逻辑或大量计算时。) - 原型对象的冗余属性(由于
Fun2.prototype = new Fun1(),子类的原型对象会包含父类实例的所有属性,这可能会导致不必要的内存占用。)
4.原型式继承
let parent = {name: 'parent',friends: [1, 2, 3],getName: function () {return this.name},
}
let child = Object.create(parent)
child.name = 'child'
child.friends.push(666)
console.log(parent.getName(), child.getName()) // parent child
console.log(parent.friends, child.friends) // [1, 2, 3, 666] [1, 2, 3, 666]
原型式继承的弊端
-
共享引用类型属性(如果父对象中的属性是引用类型(如数组、对象),所有子对象会共享这些属性。修改一个子对象的引用类型属性会影响到其他子对象。)
- 无法传递参数(原型式继承不能像构造函数继承那样在创建子对象时传递参数给父对象的初始化逻辑。)
-
缺乏构造函数支持(原型式继承没有构造函数的概念,因此无法像类或构造函数那样进行复杂的初始化操作。)
-
难以扩展和维护(使用原型式继承的代码结构相对简单,但在大型项目中可能会导致代码难以扩展和维护。特别是当需要添加更多复杂的功能或逻辑时,原型式继承的方式显得不够灵活。)
-
性能问题(由于所有子对象共享父对象的属性和方法,查找属性或方法时需要遍历原型链,这可能会影响性能,尤其是在多层继承的情况下。)
5.寄生式继承
let parent = {name: 'parent',friends: [1, 2, 3],getName: function () {return this.name},
}
function clone(params) {let copyChild = Object.create(params)copyChild.getFriends = function () {return this.friends}return copyChild
}let child = clone(parent)
child.name = 'child'
child.friends.push(999)console.log(parent.friends, child.friends) // [1, 2, 3, 999] [1, 2, 3, 999]
console.log(child.getFriends())
寄生式继承的弊端
-
代码复杂度增加(寄生式继承增加了代码的复杂性,因为它引入了一个额外的函数来封装继承逻辑。这使得代码更难理解和维护,尤其是在大型项目中。)
-
共享引用类型属性(尽管寄生式继承可以增强对象,但它仍然无法解决原型式继承中的共享引用类型属性问题。所有子对象会共享父对象中的引用类型属性,修改一个子对象的引用类型属性会影响到其他子对象。)
-
缺乏构造函数支持(寄生式继承没有构造函数的概念,因此无法像类或构造函数那样进行复杂的初始化操作。每次创建新对象时都需要调用封装函数,并且需要手动传递参数。)
-
性能问题(由于每次创建新对象时都需要调用封装函数并执行额外的逻辑,这可能会导致性能开销,尤其是在频繁创建对象的情况下。)
-
难以扩展和维护(寄生式继承的代码结构相对复杂,特别是在多层继承或需要添加更多功能时,代码会变得难以扩展和维护。此外,调试和理解这种继承模式也更加困难。)
6.寄生组合式继承
function Parent() {this.name = 'parent'this.play = [1, 2, 3]
}Parent.prototype.getName = function () {return this.name
}function Child() {Parent.call(this) // 借用构造函数继承属性this.friends = 'child'
}function clone(parent, child) {child.prototype = Object.create(parent.prototype) // 设置子类原型为父类原型的副本child.prototype.constructor = child // 修复构造函数指针
}clone(Parent, Child)Child.prototype.getFriends = function () {return this.friends
}let person = new Child()
console.log(person.getName(), person.getFriends()) // parent child
寄生组合式继承:是一种高效的继承模式,结合了构造函数继承和原型链继承的优点,避免了它们各自的缺点。它确保了每个子类实例都有自己的属性副本,同时可以继承父类原型上的方法,并且不会调用两次父类构造函数。这种继承方式在现代 JavaScript 开发中非常常见,特别是在需要复杂继承结构的情况下。
7.通过Es6中 extends 关键字继承
class Parent {constructor() {this.name = 'Parent'this.play = [1, 2, 3]}getName() {return this.name}
}class Child extends Parent {constructor() {// 在子类构造函数中调用父类构造函数,确保继承父类的实例属性,允许子类覆盖或扩展父类的功能。super() this.friends = 'child'}getFriends() {return this.friends}
}
相当于是 寄生组合式继承 的语法糖,使用起来更简洁高效。

相关文章:
关于JS继承的七种方式和理解
1.原型链继承 function Fun1() {this.name parentthis.play [1, 2, 3] } function Fun2() {this.type child }Fun2.prototype new Fun1()let s1 new Fun2() let s2 new Fun2() s1.play.push(4) console.log(s1.play, s2.play) // [1, 2, 3, 4] [1, 2, 3, 4]可以看到两个…...
储能系统-系统架构
已更新系列文章包括104、61850、modbus 、单片机等,欢迎关注 IEC61850实现方案和测试-1-CSDN博客 快速了解104协议-CSDN博客 104调试工具2_104协议调试工具-CSDN博客 1 电池储能系统(BESS) 架构 电池储能系统主要包括、电池、pcs、本地控制…...
AI智算-k8s部署DeepSeek Janus-Pro-7B 多模态大模型
文章目录 简介环境依赖模型下载下载Janus库GPU环境镜像模型manifest调用Janus多模态文生图 简介 DeepSeek Janus Pro 作为一款强大的多模态理解与生成框架,正在成为研究人员和开发者的热门选择。本文将详细介绍如何在云原生k8s环境中部署配置和使用 DeepSeek Janus…...
【截图】selenium自动通过浏览器截取指定元素div的图片
【截图】selenium自动通过浏览器截取指定元素div的图片 思路 截取完整网页截图 通过元素的坐标 截图到指定位置的图片 前提是已经获取到 driver 了 # 定位目标divtarget_div driver.find_element(By.CLASS_NAME, headlines-right)# 获取div的位置和大小location target_div…...
如何导入第三方sdk | 引入第三方jar 包
0. 背景1. 上传私有仓库2. 使用本地文件系统 0. 背景 对接一些第三方功能,会拿到第三方的sdk,也就是jar包,如何导入呢 1. 上传私有仓库 最好的方式就是将第三方jar包,上传到私有的仓库,这样直接正常在pom引用即可如果只…...
HarmonyOS 5.0应用开发——ContentSlot的使用
【高心星出品】 文章目录 ContentSlot的使用使用方法案例运行结果 完整代码 ContentSlot的使用 用于渲染并管理Native层使用C-API创建的组件同时也支持ArkTS创建的NodeContent对象。 支持混合模式开发,当容器是ArkTS组件,子组件在Native侧创建时&#…...
C#常用集合优缺点对比
先上结论: 在C#中,链表、一维数组、字典、List<T>和ArrayList是常见的数据集合类型,它们各有优缺点,适用于不同的场景。以下是它们的比较: 1. 一维数组 (T[]) 优点: 性能高:数组在内存中…...
基于CLIP视觉语言大模型的行人重识别方法的简单框架设计
以下是一个基于CLIP视觉语言大模型的行人重识别方法的简单框架设计,用于数据集测试。我们将使用torch和clip库,假设数据集是一个包含行人图像的文件夹结构,每个子文件夹代表一个行人身份。 步骤概述 安装必要的库加载CLIP模型定义数据集类提…...
RabbitMQ 从入门到精通:从工作模式到集群部署实战(三)
文章目录 使用CLI管理RabbitMQrabbitmqctlrabbitmq-queuesrabbitmq-diagnosticsrabbitmq-pluginsrabbitmq-streamsrabbitmq-upgraderabbitmqadmin 使用CLI管理RabbitMQ RabbitMQ CLI 工具需要安装兼容的 Erlang/OTP版本。 这些工具假定系统区域设置为 UTF-8(例如en…...
BurpSuite抓包与HTTP基础
文章目录 前言一、BurpSuite1.BurpSuite简介2.BurpSuite安装教程(1)BurpSuite安装与激活(2)安装 https 证书 3.BurpSuite使用4.BurpSuite资料 二、图解HTTP1.HTTP基础知识2.HTTP客户端请求消息3.HTTP服务端响应消息4.HTTP部分请求方法理解5.HTTPS与HTTP 总结 前言 在网络安全和…...
SQL Server 数据库迁移到 MySQL 的完整指南
文章目录 引言一、迁移前的准备工作1.1 确定迁移范围1.2 评估兼容性1.3 备份数据 二、迁移工具的选择2.1 使用 MySQL Workbench2.2 使用第三方工具2.3 手动迁移 三、迁移步骤3.1 导出 SQL Server 数据库结构3.2 转换数据类型和语法3.3 导入 MySQL 数据库3.4 迁移数据3.5 迁移存…...
【大模型】DeepSeek与chatGPT的区别以及自身的优势
目录 一、前言二、核心技术对比2.1 模型架构设计2.1.1 ChatGPT的Transformer架构2.1.2 DeepSeek的混合架构 2.2 训练数据体系2.2.1 ChatGPT的数据特征2.2.2 DeepSeek的数据策略 三、应用场景对比3.1 通用场景表现3.1.1 ChatGPT的强项领域3.2.2 DeepSeek的专项突破 3.3 响应效率…...
DeepSeek:知识图谱与大模型参数化知识融合的创新架构
引言:AI 领域的融合趋势 在目前大模型与知识图谱作为两个重要的研究方向,各自展现出了强大的能力与潜力。大模型,凭借其在海量数据上的深度训练,拥有强大的语言理解与生成能力,能够处理多种自然语言处理任务࿰…...
ES6 迭代器 (`Iterator`)使用总结
Iterator(迭代器)是 ES6 引入的一种 接口,用于 顺序访问 可迭代对象(Array、Set、Map、String、arguments、自定义对象等)。 Iterator(迭代器)的作用有三个: 为各种数据结构提供一个…...
信用修复和失联修复的区别
失联修复和信用修复是两个不同的概念,在目的、操作方式和应用场景上都有所区别。 失联修复 失联修复主要是指在金融催收行业中,当债务人的联系方式(通常是手机号码)发生改变,导致无法联系到债务人时,催收公…...
2025蓝桥杯JAVA编程题练习Day3
1.黛玉泡茶【算法赛】 问题描述 话说林黛玉闲来无事,打算在潇湘馆摆个茶局,邀上宝钗、探春她们一起品茗赏花。黛玉素来讲究,用的茶杯也各有不同,大的小的,高的矮的,煞是好看。这不,她从柜子里…...
[论文阅读] Knowledge Fusion of Large Language Models
Knowledge Fusion of Large Language Models (FuseLLM) Methodology 整体Pipeline如下图所示 不同的动物代表不同的LLM。左边第一,第二分别是Ensemble以及Weight Merging方法。最右侧为本文提出的FuseLLM。 Ensemble: 融合多个models的预测结果,比如…...
deepseek来讲lua
Lua 是一种轻量级、高效、可嵌入的脚本语言,广泛应用于游戏开发、嵌入式系统、Web 服务器等领域。以下是 Lua 的主要特点和一些基本概念: 1. 特点 轻量级:Lua 的核心非常小,适合嵌入到其他应用程序中。高效:Lua 的执…...
探索 Spring Cloud Alibaba:开启微服务架构新时代
一、引言 在当今数字化浪潮中,软件系统的规模和复杂度不断攀升,传统的单体架构逐渐难以满足快速迭代、高并发处理以及灵活扩展的需求。微服务架构应运而生,它将一个大型的应用拆分成多个小型、自治的服务,每个服务专注于特定的业务…...
【数据结构】(6) LinkedList 链表
一、什么是链表 1、链表与顺序表对比 不同点LinkedListArrayList物理存储上不连续连续随机访问效率O(N)O(1)插入、删除效率O(1)O(N) 3、链表的分类 链表根据结构分类,可分为单向/双向、无头结点/有头节点、非循环/循环链表,这三组每组各取…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
