javascript基础九:说说Javascript中的继承?如何实现继承?

news2025/1/11 11:03:10

在这里插入图片描述
一、是什么
继承(inheritance)是面向对象软件技术当中的一个概念
如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”

继承的优点

继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码

在子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能

虽然JavaScript并不是真正的面向对象语言,但它天生的灵活性,使应用场景更加丰富

关于继承,举个粟子:

定义一个类(Class)叫汽车,汽车的属性包括颜色、轮胎、品牌、速度、排气量等

class Car{
    constructor(color,speed){
        this.color = color
        this.speed = speed
        // ...
    }
}

由汽车这个类可以派生出“轿车”和“货车”两个类,在汽车的基础属性上,为轿车添加一个后备厢、给货车添加一个大货箱

// 货车
class Truck extends Car{
    constructor(color,speed){
        super(color,speed)
        this.Container = true // 货箱
    }
}

这样轿车和货车就是不一样的,但是二者都属于汽车这个类,汽车、轿车继承了汽车的属性,而不需要再次在“轿车”中定义汽车已经有的属性

在“轿车”继承“汽车”的同时,也可以重新定义汽车的某些属性,并重写或覆盖某些属性和方法,使其获得与“汽车”这个父类不同的属性和方法

class Truck extends Car{
    constructor(color,speed){
        super(color,speed)
        this.color = "black" //覆盖
        this.Container = true // 货箱
    }
}

从这个例子中就能详细说明汽车、轿车以及卡车之间的继承关系

二、实现方式

下面给出JavaScripy常见的继承方式:

  • 原型链继承
  • 构造函数继承(借助 call)
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

原型链继承

原型链继承是比较常见的继承方式之一,其中涉及的构造函数、原型和实例,三者之间存在着一定的关系,即每一个构造函数都有一个原型对象,原型对象又包含一个指向构造函数的指针,而实例则包含一个原型对象的指针

举个粟子

function Parent(){
    this.name="小爱同学";
    this.arr = [1,2,3]
}
undefined
function child(){
    this.type = "child"
}
undefined
child.prototype = new Parent()
Parent {name: '小爱同学', arr: Array(3)}
var child1 = new child()
undefined
var child2 = new child()
undefined
child1.arr.push(4)
4
child2.arr
(4) [1, 2, 3, 4]

在这里插入图片描述
改变child1的arr属性,会发现child2也跟着发生变化了,这是因为两个实例使用的是同一个原型对象,内存空间是共享的

构造函数继承
借助 call调用Parent函数

function Parent(){
    this.name = 'parent1';
}

Parent.prototype.getName = function () {
    return this.name;
}

function Child(){
    Parent1.call(this);
    this.type = 'child'
}

let child = new Child();
console.log(child);  // 没问题
console.log(child.getName());  // 会报错

可以看到,父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法

相比第一种原型链继承方式,父类的引用属性不会被共享,优化了第一种继承方式的弊端,但是只能继承父类的实例属性和方法,不能继承原型属性或者方法

组合继承

前面我们讲到两种继承方式,各有优缺点。组合继承则将前两种方式继承起来\

function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
}

Parent3.prototype.getName = function () {
    return this.name;
}
function Child3() {
    // 第二次调用 Parent3()
    Parent3.call(this);
    this.type = 'child3';
}

// 第一次调用 Parent3()
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);  // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'

这种方式看起来就没什么问题,方式一和方式二的问题都解决了,但是从上面代码我们也可以看到Parent3 执行了两次,造成了多构造一次的性能开销

原型式继承

这里主要借助Object.create方法实现普通对象的继承
举个粟子

let parent4 = {
    name: "parent4",
    friends: ["p1", "p2", "p3"],
    getName: function() {
      return this.name;
    }
  };

  let person4 = Object.create(parent4);
  person4.name = "tom";
  person4.friends.push("jerry");

  let person5 = Object.create(parent4);
  person5.friends.push("lucy");

  console.log(person4.name); // tom
  console.log(person4.name === person4.getName()); // true
  console.log(person5.name); // parent4
  console.log(person4.friends); // ["p1", "p2", "p3","jerry","lucy"]
  console.log(person5.friends); // ["p1", "p2", "p3","jerry","lucy"]

这种继承方式的缺点也很明显,因为Object.create方法实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能

寄生式继承
寄生式继承在上面继承基础上进行优化,利用这个浅拷贝的能力再进行增强,添加一些方法

let parent5 = {
    name: "parent5",
    friends: ["p1", "p2", "p3"],
    getName: function() {
        return this.name;
    }
};

function clone(original) {
    let clone = Object.create(original);
    clone.getFriends = function() {
        return this.friends;
    };
    return clone;
}

let person5 = clone(parent5);

console.log(person5.getName()); // parent5
console.log(person5.getFriends()); // ["p1", "p2", "p3"]

其优缺点也很明显,跟上面讲的原型式继承一样

寄生组合式继承
寄生组合式继承,借助解决普通对象的继承问题的Object.create 方法,在几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式

function clone (parent, child) {
    // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
    child.prototype = Object.create(parent.prototype);
    child.prototype.constructor = child;
}

function Parent6() {
    this.name = 'parent6';
    this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
    return this.name;
}
function Child6() {
    Parent6.call(this);
    this.friends = 'child5';
}

clone(Parent6, Child6);

Child6.prototype.getFriends = function () {
    return this.friends;
}

let person6 = new Child6(); 
console.log(person6); //{friends:"child5",name:"child5",play:[1,2,3],__proto__:Parent6}
console.log(person6.getName()); // parent6
console.log(person6.getFriends()); // child5

可以看到 person6 打印出来的结果,属性都得到了继承,方法也没问题

文章一开头,我们是使用ES6 中的extends关键字直接实现 JavaScript的继承

class Person {
  constructor(name) {
    this.name = name
  }
  // 原型方法
  // 即 Person.prototype.getName = function() { }
  // 下面可以简写为 getName() {...}
  getName = function () {
    console.log('Person:', this.name)
  }
}
class Gamer extends Person {
  constructor(name, age) {
    // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
    super(name)
    this.age = age
  }
}
const asuna = new Gamer('Asuna', 20)
asuna.getName() // 成功访问到父类的方法

利用babel工具进行转换,我们会发现extends实际采用的也是寄生组合继承方式,因此也证明了这种方式是较优的解决继承的方式

三、总结
下面以一张图作为总结:
在这里插入图片描述
通过Object.create 来划分不同的继承方式,最后的寄生式组合继承方式是通过组合继承改造之后的最优继承方式,而 extends 的语法糖和寄生组合继承的方式基本类似

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

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

相关文章

[LeetCode周赛复盘] 第 105 场双周赛20230528

[LeetCode周赛复盘] 第 105 场双周赛20230528 一、本周周赛总结6395. 购买两块巧克力1. 题目描述2. 思路分析3. 代码实现 6394. 字符串中的额外字符1. 题目描述2. 思路分析3. 代码实现 6393. 一个小组的最大实力值1. 题目描述2. 思路分析3. 代码实现 6464. 最大公约数遍历1. 题…

性能测试需求分析有哪些?怎么做?

目录 性能测试必要性评估 常见性能测试关键评估项如下: 性能测试工具选型 性能测试需求分析 性能测试需求评审 性能测试需求分析与传统的功能测试需求有所不同,功能测试需求分析重点在于从用户层面分析被测对象的功能性、易用性等质量特性&#xff…

【Go语言从入门到实战】并发篇

Go语言从入门到实战 — 并发篇 协程 Thread vs Groutine 相比之下,协程的栈大小就小很多了,创建起来也会更快,也更节省系统资源。 一个 goroutine 的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量…

2023 年面向初学者的 5 大自由写作技巧

在这篇文章中,我们将讨论初学者的自由写作技巧 译自:https://jitendra.co/freelance-writing-tips-for-beginners/ 比较平易近人,在做独立站并且自己写原创时候可以参考下,面对的甲方爸爸不同而已 最初的兼职活动最终成为我生活中…

Docker下安装MySQL,PostgreSQL,SQL Server(包含离线和在线安装)

1 MySQL 1.1 离线安装 1.1.1 加载镜像 使用ftp工具将安装包上传至服务器最大目录下,这里以根目录为最大目录举例 键入加载镜像命令:docker load -i 镜像包名称 docker load -i mysql80.tar使用docker images命令查看已经加载的镜像,如上…

【利用ChatGPT学习英语口语(包括如何安装插件的详细教程)】

内容目录 一、插入ChatGPT语音播放插件1. 打开谷歌浏览器——右上角三点——更多工具——扩展程序2. 点击扩展程序三个横杠——打开chrome 应用商店3. 搜索Voice Control for ChatGPT——点击如下图第一个——添加至Chrome——添加扩展程序4. 成功安装,打开ChatGPT有…

【大数据hive】hive视图与物化视图使用详解

目录 一、hive中的视图 二、hive视图语法与操作 2.1 数据准备 2.2 创建视图 2.2.1 创建普通的视图 2.2.2 基于视图创建视图 2.3 查看视图定义 2.4 使用视图 2.5 删除视图 2.6 更改视图属性 2.7 更改视图定义 三、使用视图的好处 3.1 只将真实表中特定的列数据提…

线程同步模式的设计思路

👳我亲爱的各位大佬们好😘😘😘 ♨️本篇文章记录的为 JDK8 新特性 Stream API 进阶 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。 …

ISCC2023 擂台misc wp

刚想起来发 文章目录 雪豹哦?摩斯密码?ඞG9的钢琴曲BNG听你心跳里的狂Brain Games user-id:芝士雪豹 雪豹 这道题没啥意思,存粹为了套拿450分。知识点属于重复知识点,见谅: 拿到附件,用360解…

分布式网络通信框架(十)——Mprpc框架使用示例

发布一个服务提供远程调用方法的流程 若想要发布一个服务提供一些远程调用方法,步骤如下: 先在protobuf文件中添加参数和返回值的message 类型,然后再添加希望提供的服务 service 类型(如UserServiceRpc)和 其中的方…

傅里叶级数和傅里叶变换之间的关系推理及应用

傅里叶级数和傅立叶变换是傅里叶分析的两个主要工具,它们之间有密切的关系。 什么是傅里叶级数 傅里叶级数是将一个周期函数分解为一系列正弦和余弦函数的和。它适用于周期性信号,可以将周期函数表示为一组振幅和相位不同的谐波分量的和。傅里叶级数展…

【ChatGPT】ChatGPT快速生成短视频

1.chatGPT剪映 chatGPT生成文本后通过剪映图文成片 这次用了new bing:Chatbot AI 在线网页版 (atmob.cn) 打开剪映-图文成片 把new bing生成的文本粘贴过来,点击生成视频。 生成好了,是这样 剪映自动生成的,最后还是得手工改改&…

ChatGPT生成Excel统计公式

🍏🍐🍊🍑🍒🍓🫐🥑🍋🍉 ChatGPT生成Excel统计公式 文章目录 🍐问题引入🍐具体操作🐳结语 🍐问题引入…

如何使用 service account 获取 keycloak 的用户信息

Keycloak 是一个开源的权限管理和认证系统。使用 Keycloak 可以让开发者专注于解决业务的核心问题。获取用户信息是权限管理和认证系统需要的基本功能。Service Account 是OAuth 2.0推荐的系统服务使用的账户,开发者可以通过 Keycloak 的 Service Account 来让自己的…

【嵌入式烧录/刷写文件】-3.2-S19/Hex文件转换为Bin文件

案例背景(共6页精讲): 该篇将告诉您:如何使用Vector HexView工具,对一个Intel Hex或Motorola S-record(S19/SREC/mot/SX)文件转换为bin文件。 目录 1 Intel Hex,Motorola S-record(S19/SREC/mot/SX),Bin文件之间的差…

基于SpringBoot养老院管理系统

目录 一、项目介绍 二. 运行环境 三、项目技术 四、部署项目 五、项目运行 六、项目展示 五、项目下载 一、项目介绍 基于springboot的养老院管理系统拥有多种角色账号:管理员和用户 管理员:管理员管理、用户管理、健康管理、病例方案管理、药品…

计算机网络面试八股文

网络分层结构 计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。最全面的Java面试网站:最全面的Java面试网站 五层模型:应用层、传输层、网络层、数据链路层、物理层。 应用层&a…

文生视频综述

文字生成视频当前挑战 和发展现状_哔哩哔哩_bilibili今天我们聊了什么是 text to video,它的原理和目前的研究进展。text to video 是一种将文本转换为视频的技术,它可以通过图像处理、语音识别和自然语言处理等技术来实现。目前,text to vid…

【C语言】刷题训练营 —— 每日一练(十三)

文章目录 前言 BC123 小乐乐找最大数BC124 小乐乐是否被叫家长BC125 小乐乐转换成绩BC126 小乐乐算多少人被请家长BC127 小乐乐算最高分BC128 小乐乐计算求和BC129 小乐乐计算函数BC130 小乐乐查找数字BC131 KiKi学程序设计基础BC132 KiKi算期末成绩完结 前言 大家好&#xff…

计算机体系结构标量处理机

先行控制技术 缓冲深度的设计方法 以先行指令缓冲栈为例。 假设缓冲深度为 D 1 D_1 D1​,考虑以下两种极端情况。 (1)先行指令缓冲栈已经充满,此时指令流出速度最快,例如连续分析RR型指令 ,设这种指令序…