关于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]可以看到两个…...
Elasticsearch的使用场景、数据量级及选择原因?为什么没有用Hbase?
目录 Elasticsearch 的使用场景、数据量级及选择原因 1. Elasticsearch 的使用场景 2. 数据量级 3. 选择 Elasticsearch 的原因 4. 为什么选择 Elasticsearch 而不是 HBase 5. 数据量级对比 6. 总结 Elasticsearch 的使用场景、数据量级及选择原因 1. Elasticsearch 的…...
Oracle迁移到MySQL
Oracle迁移到MySQL业务需要全面改造适配,数据库对象和业务SQL语法需要一对一映射分析如何改写,根据业务使用实际情况评估改造适配成本较高。 目前,已有数据库产品能力缺少自动化迁移工具,需要依赖生态产品能力,比如云和…...
.Net Core笔记知识点(跨域、缓存)
设置前端跨域配置示例: builder.Services.AddCors(option > {option.AddDefaultPolicy(policy > {policy.WithOrigins(originUrls).AllowAnyMethod().AllowAnyHeader().AllowCredentials();});});var app builder.Build();app.UseCors(); 【客户端缓存】接…...
【Vue】在Vue3中使用Echarts的示例 两种方法
文章目录 方法一template渲染部分js部分方法一实现效果 方法二template部分js or ts部分方法二实现效果 贴个地址~ Apache ECharts官网地址 Apache ECharts示例地址 官网有的时候示例显示不出来,属于正常现象,多进几次就行 开始使用前,记得先…...
每日Attention学习18——Grouped Attention Gate
模块出处 [ICLR 25 Submission] [link] UltraLightUNet: Rethinking U-shaped Network with Multi-kernel Lightweight Convolutions for Medical Image Segmentation 模块名称 Grouped Attention Gate (GAG) 模块作用 轻量特征融合 模块结构 模块特点 特征融合前使用Group…...
Qt —— 加载百度离线地图、及简单绘图(附源码)
效果 说明 软件代码已下载了某区域的离线瓦片地图,通过百度离线api进行调用的地图效果。 源码 void PointMapTEST...
124,【8】buuctf web [极客大挑战 2019] Http
进入靶场 查看源码 点击 与url有关,抓包 over...
源路由 | 源路由网桥 / 生成树网桥
注:本文为 “源路由” 相关文章合辑。 未整理去重。 什么是源路由(source routing)? yzx99 于 2021-02-23 09:45:51 发布 考虑到一个网络节点 A 从路由器 R1 出发,可以经过两台路由器 R2、R3,到达相同的…...
c#中lock的经典示例
lock 是 C# 中的一种用于同步线程执行的机制,它帮助确保多个线程在访问共享资源时不会发生冲突或数据损坏。其作用是通过给临界区(即多线程访问共享资源的代码段)加锁,使得在同一时刻只能有一个线程进入执行该代码段。 1、lock 的…...
python编程-集合内置函数和filter(),集合常见操作
在Python中,列表、集合、字典是三种常用的数据结构,它们各自拥有一些内置函数,用于执行各种操作。 一、列表的常用内置函数 #1、append(obj): 在列表末尾添加新的对象。list_a [1, 2, 3] list_a.append(4) print(list_a) # 输出: [1,…...
蓝桥杯Java之输入输出练习题
题目 1:多组AB(基础版) 题目描述: 输入多组数据,每组数据包含两个整数 A 和 B,计算它们的和。输入以 文件结尾(EOF) 结束。 输入格式: 每行包含两个整数 A 和 B&#x…...
FPGA的IP核接口引脚含义-快解
疑问 手册繁琐,怎样快速了解IP核各输入输出接口引脚的含义。 答疑 不慌不慌,手册确实比较详细但繁琐,如何快速知晓该部分信息,涛tao道长给你们说,简单得很,一般新入门的道友有所不知,往往后面…...
Linux的循环,bash的循环
Linux的循环,bash的循环 在 Linux 系统中,Bash 循环是最常用的循环实现方式(Bash 是 Linux 默认的 Shell),但广义上“Linux 的循环”可能涉及其他 Shell 或编程语言的循环结构。以下是 Bash 循环的详细解析及其在 Linux 环境中的…...
Qwen2-VL-2B-Instruct 模型 RK3576 板端部署过程
需要先在电脑上运行 RKLLM-Toolkit 工具,将训练好的模型转换为 RKLLM 格式的模型,然后使用 RKLLM C API 在开发板上进行推理。 在安装前先查看板端的内存容量,和自己模型占用大小比较一下,别安装编译好了不能用。 这里我就是先尝试…...
如何设计光耦电路
光耦长这样,相信小伙伴们都见过,下图是最为常用的型号PC817 怎么用?我们先看图,如下图1: Vin为输入信号,一般接MCU的GPIO口,由于这里的VCC1为3.3V,故MCU这边的供电电源不能超过3.3V…...
ADC模数转换器概念函数及应用
ADC模数转换器概念函数及应用 文章目录 ADC模数转换器概念函数及应用1.ADC简介2.逐次逼近型ADC2.1逐次逼近型ADC2.2stm32逐次逼近型2.3ADC基本结构2.4十六个通道 3.规则组的4种转换模式3.1单次转换,非扫描模式3.2连续转换,非扫描模式3.3单次转换…...
DFX(Design for eXcellence)架构设计全解析:理论、实战、案例与面试指南*
一、什么是 DFX ?为什么重要? DFX(Design for eXcellence,卓越设计)是一种面向产品全生命周期的设计理念,旨在确保产品在设计阶段就具备**良好的制造性(DFM)、可测试性(…...
【LeetCode】152、乘积最大子数组
【LeetCode】152、乘积最大子数组 文章目录 一、dp1.1 dp1.2 简化代码 二、多语言解法 一、dp 1.1 dp 从前向后遍历, 当遍历到 nums[i] 时, 有如下三种情况 能得到最大值: 只使用 nums[i], 例如 [0.1, 0.3, 0.2, 100] 则 [100] 是最大值使用 max(nums[0…i-1]) * nums[i], 例…...
Doris更新某一列数据完整教程
在Doris,要更新数据,并不像mysql等关系型数据库那样方便,可以用update set来直接更新某个列。在Doris只能进行有限的更新,官方文档如下: UPDATE - Apache Doris 1、使用Doris自带的Update功能 描述 该语句是为进行对数据进行更新的操作,UPDATE 语句目前仅支持 UNIQUE…...
《云夹:让书签管理变得轻松又高效》
在当今数字化的生活与工作场景中,我们畅游于网络的浩瀚海洋,每天都会邂逅各式各样有价值的网页内容。而如何妥善管理这些如繁星般的书签,使其能在我们需要时迅速被找到,已然成为众多网络使用者关注的焦点。云夹,作为一…...
Microsoft Fabric - 尝试一下在pipeline中发送请求给web api(获取数据和更新数据)
1.简单介绍 Microsoft Fabric中的Pipeline支持很多种activity,分成数据转换和控制流两种类型的activitly。 这边将尝试一下发送web请求的activity,要做成的pipeline大概如下图所示, 上图中有4个Activity,作用如下 Web - 从一个…...
Ruby:从宝石到编程语言的奇妙联系(中英双语)
Ruby:从宝石到编程语言的奇妙联系 在珠宝世界中,红宝石(Ruby)是一种象征热情、力量和高贵的珍贵宝石;而在编程世界中,Ruby则是一门灵活、优雅且富有创造力的编程语言。那么,这两者究竟有何联系…...
如何对java 源码进行分析, 统计出每个方法被引用的次数
使用 JavaParser 统计源码中每个方法的引用次数是一个可行且有效的方法。JavaParser 是一个用于解析 Java 源代码并生成抽象语法树(AST)的库,它提供了丰富的 API 来遍历和操作 AST。下面是一个详细的指南,说明如何使用 JavaParser…...
数据完整性与约束的分类
一、引言 为什么需要约束?为了保证数据的完整性。 (1)数据完整性 数据完整性指的是数据的精确性和可靠性。 为了保证数据的完整性,SQL对表数据进行额外的条件限制,从以下四方面考虑: ①实体完整性&…...
docker安装nacos2.x
本文为单机模式,非集群教程,埋坑 nacos2.x官方强制条件 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。 64 bit JDK 1.8 Maven 3.2.x 环境介绍 centos 7 maven 3.9.9 jdk 17 nacos 2.3.1 1. 拉取docker镜像 d…...
git代理设置
在 Git 中,可以通过以下命令查看当前设置的代理配置: 查看 HTTP 代理 git config --get http.proxy查看 HTTPS 代理 git config --get https.proxy查看全局代理设置 如果你设置了全局代理,可以通过以下命令查看: git config …...
python编程-内置函数bin(),bool(),abs() ,all(),any(),ascii(),max(),min() 详解
1、bin()函数用于将整数转换为其二进制字符串表示。并返回一个以0b开头的字符串,表示该整数的二进制形式。 # 十进制数转换为二进制字符串 decimal_number 42 binary_string bin(decimal_number) print(f"Decimal {decimal_number} is {binary_string} in b…...
GB/T28181 开源日记[8]:国标开发速知速会
服务端源代码 github.com/gowvp/gb28181 前端源代码 github.com/gowvp/gb28181_web 介绍 go wvp 是 Go 语言实现的开源 GB28181 解决方案,基于GB28181-2022标准实现的网络视频平台,支持 rtmp/rtsp,客户端支持网页版本和安卓 App。支持rts…...
6 maven工具的使用、maven项目中使用日志
文章目录 前言一、maven:一款管理和构建java项目的工具1 基本概念2 maven的安装与配置(1)maven的安装(2)IDEA集成Maven配置当前项目工程设置 maven全局设置 (3)创建一个maven项目 3 pom.xml文件…...
