关于JS继承的七种方式和理解

news2025/2/8 8:58:13

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]

可以看到两个子类实例的属性都被影响了,这是因为通过原型链继承的子类实例的属性是共享的

原型链继承的弊端

  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

可以看到构造函数继承确实不会共享属性了,但是构造函数继承只继承了父类的实例属性和方法,而不会继承父类原型上的方法

构造函数继承的弊端

  1. 无法复用父类原型上的方法
  2. 每个子类实例都会创建新的父类实例(每次创建子类实例时,都会调用一次父类构造函数,这会导致不必要的资源消耗,尤其是在父类构造函数中有复杂初始化逻辑或大量计算时。
  3. 无法传递参数给父类构造函数(如果父类构造函数需要参数,子类构造函数必须显式地传递这些参数,否则父类构造函数中的初始化逻辑可能无法正常工作。
  4. 难以维护和调试(构造函数继承的代码结构相对复杂,特别是在多层继承的情况下,可能会导致代码难以理解和维护。此外,调试时也更难追踪继承链中的问题。

3.组合继承(伪经典继承)

function Fun1(name) {
  this.name = name
  this.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) // false

child1.play.push(4)
console.log(child1.play, child2.play) // [1, 2, 3, 4] [1, 2, 3]

console.log(child1.getName(), child2.getName()) // haha haha

组合继承的优点

  1. 避免了原型链继承的共享属性问题(每个子类实例都有自己的属性副本,不会共享父类实例中的引用类型属性。)
  2. 可以继承父类原型上的方法(子类不仅继承了父类的实例属性,还可以继承父类原型上的方法,避免了重复定义方法的问题。)
  3. 灵活性高(可以在子类构造函数中调用父类构造函数时传递参数,确保父类初始化逻辑正常工作。)

组合继承的缺点

  1. 调用两次父类构造函数(组合继承的主要问题是它会调用两次父类构造函数:一次是在创建子类原型时 (new Fun1()),另一次是在子类构造函数中 (Fun1.call(this, 'haha'))。这会导致不必要的性能开销,尤其是在父类构造函数中有复杂初始化逻辑或大量计算时。)
  2. 原型对象的冗余属性(由于 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]

原型式继承的弊端

  1. 共享引用类型属性(如果父对象中的属性是引用类型(如数组、对象),所有子对象会共享这些属性。修改一个子对象的引用类型属性会影响到其他子对象。
  2. 无法传递参数(原型式继承不能像构造函数继承那样在创建子对象时传递参数给父对象的初始化逻辑。)
  3. 缺乏构造函数支持(原型式继承没有构造函数的概念,因此无法像类或构造函数那样进行复杂的初始化操作。
  4.  难以扩展和维护(使用原型式继承的代码结构相对简单,但在大型项目中可能会导致代码难以扩展和维护。特别是当需要添加更多复杂的功能或逻辑时,原型式继承的方式显得不够灵活。
  5. 性能问题(由于所有子对象共享父对象的属性和方法,查找属性或方法时需要遍历原型链,这可能会影响性能,尤其是在多层继承的情况下。

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())

寄生式继承的弊端

  1. 代码复杂度增加(寄生式继承增加了代码的复杂性,因为它引入了一个额外的函数来封装继承逻辑。这使得代码更难理解和维护,尤其是在大型项目中。
  2. 共享引用类型属性(尽管寄生式继承可以增强对象,但它仍然无法解决原型式继承中的共享引用类型属性问题。所有子对象会共享父对象中的引用类型属性,修改一个子对象的引用类型属性会影响到其他子对象。
  3. 缺乏构造函数支持(寄生式继承没有构造函数的概念,因此无法像类或构造函数那样进行复杂的初始化操作。每次创建新对象时都需要调用封装函数,并且需要手动传递参数。
  4. 性能问题(由于每次创建新对象时都需要调用封装函数并执行额外的逻辑,这可能会导致性能开销,尤其是在频繁创建对象的情况下。
  5. 难以扩展和维护(寄生式继承的代码结构相对复杂,特别是在多层继承或需要添加更多功能时,代码会变得难以扩展和维护。此外,调试和理解这种继承模式也更加困难。

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
  }
}

相当于是 寄生组合式继承 的语法糖,使用起来更简洁高效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2294705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【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…

124,【8】buuctf web [极客大挑战 2019] Http

进入靶场 查看源码 点击 与url有关,抓包 over

源路由 | 源路由网桥 / 生成树网桥

注:本文为 “源路由” 相关文章合辑。 未整理去重。 什么是源路由(source routing)? yzx99 于 2021-02-23 09:45:51 发布 考虑到一个网络节点 A 从路由器 R1 出发,可以经过两台路由器 R2、R3,到达相同的…

FPGA的IP核接口引脚含义-快解

疑问 手册繁琐,怎样快速了解IP核各输入输出接口引脚的含义。 答疑 不慌不慌,手册确实比较详细但繁琐,如何快速知晓该部分信息,涛tao道长给你们说,简单得很,一般新入门的道友有所不知,往往后面…

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单次转换&#xf…

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], 例…

《云夹:让书签管理变得轻松又高效》

在当今数字化的生活与工作场景中,我们畅游于网络的浩瀚海洋,每天都会邂逅各式各样有价值的网页内容。而如何妥善管理这些如繁星般的书签,使其能在我们需要时迅速被找到,已然成为众多网络使用者关注的焦点。云夹,作为一…

Microsoft Fabric - 尝试一下在pipeline中发送请求给web api(获取数据和更新数据)

1.简单介绍 Microsoft Fabric中的Pipeline支持很多种activity,分成数据转换和控制流两种类型的activitly。 这边将尝试一下发送web请求的activity,要做成的pipeline大概如下图所示, 上图中有4个Activity,作用如下 Web - 从一个…

数据完整性与约束的分类

一、引言 为什么需要约束?为了保证数据的完整性。 (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…

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文件…

GB/T 43698-2024 《网络安全技术 软件供应链安全要求》标准解读

一、43698-2024标准图解 https://mmbiz.qpic.cn/sz_mmbiz_png/rwcfRwCticvgeBPR8TWIPywUP8nGp4IMFwwrxAHMZ9Enfp3wibNxnfichT5zs7rh2FxTZWMxz0je9TZSqQ0lNZ7lQ/640?wx_fmtpng&fromappmsg 标准在线预览: 国家标准|GB/T 43698-2024 相关标准: &a…

CF 278A.Circle Line

题目分析 输入n个数据作为路径,求从a到b的最短距离,需要将其相成一个圆圈,既可以从小往大走又可以从大往小走 思路分析 依然将数据存为数组,通过下标进行操作,既然说了有两种方式那就计算两种方式哪个更快就输出谁 代…

本地部署deepseek简单教程

部署deepseek,首先需要知道deepseek官网地址:DeepSeek 第一步:Ollama 去ollama下载对应的版本,我的电脑是window 在这里可以看到关于deepseek相关 第二步,下载完ollama无脑下一步就可以 这样属于安装成功 第三步&…

UnityShader学习笔记——多种光源

——内容源自唐老狮的shader课程 目录 1.光源类型 2.判断光源类型 2.1.在哪判断 2.2.如何判断 3.光照衰减 3.1.基本概念 3.2.unity中的光照衰减 3.3.光源空间变换矩阵 4.点光源衰减计算 5.聚光灯衰减计算 5.1.聚光灯的cookie(灯光遮罩) 5.2.聚…