Javascript如何实现继承?

#一、是什么

继承(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 = 'parent1'; this.play = [1, 2, 3] } function Child() { this.type = 'child2'; } Child1.prototype = new Parent(); console.log(new Child())

上面代码看似没问题,实际存在潜在问题

解释

var s1 = new Child2(); var s2 = new Child2(); s1.play.push(4); console.log(s1.play, s2.play); // [1,2,3,4]

改变s1play属性,会发现s2也跟着发生变化了,这是因为两个实例使用的是同一个原型对象,内存空间是共享的

#构造函数继承

借助 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.xdnf.cn/news/8993.html

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

相关文章

测度论原创(三)

Morden Prob 文章目录 Morden ProbWeek3多维扩展和随机向量定理3.1推论:random variable的变换定理3.2 连续函数的可测性定理3.3 可测函数的线性组合关于拓展实数集的延伸定理3.4 可测函数的极限依旧为可测性随机变量的概率律(Law of X X X)…

Sql面试题二:请查询出用户连续三天登录的所有数据记录

问题: 现有用户登录记录表,请查询出用户连续三天登录的所有数据记录 id dt 1 2024-04-25 1 2024-04-26 1 2024-04-27 1 2024-04-28 1 2024-04-30 1 2024-05-01 1 2024-05-02 1 2024-05-04 1 2024-05-05 2 2…

vite中env uat/dev文件项目配置

1:图示 在vscode中显示的是(在文件中显示不是文件夹而在vscode中显示是文件夹-- .env 而这个.env也是有内容的) 2:.env文件内容 # 标题 VITE_APP_TITLE管理系统# 项目本地运行端口号 VITE_PORT80# open 运行 npm run dev 时自动打…

ssm基于JAVA的网上订餐管理系统+vue

系统包含:源码论文 所用技术:SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习,获取源码看文章最下面 需要定制看文章最下面 目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容…

向日葵远程桌面Visual Studio白屏

问题描述 今天没带笔记本,想着拿 Ipad 远程写会代码,打开VS发现白屏了,看了看向日葵里面的设置有GPU加速,发现是和VS的GPU加速冲突了 解决方案(远程同样可用) ALT T 弹出工具菜单O 打开选项菜单A会取消…

Pandas | 数据分析时将特定列转换为数字类型 float64 或 int64的方法

类型转换 传统方法astype使用value_counts统计通过apply替换并使用astype转换 pd.to_numericx对连续变量进行转化⭐参数:返回值:示例代码: isnull不会检查空字符串 数据准备 有一组数据信息如下,其中主要将TotalCharges、MonthlyC…

Python+Appium编写脚本

一、环境配置 1、安装JDK,版本1.8以上 2、安装Python,版本3.x以上,用来解释python 3、安装node.js,版本^14.17.0 || ^16.13.0 || >18.0.0,用来安装Appimu Server 4、安装npm,版本>8,用…

WPF MVVM入门系列教程(三、数据绑定)

本文主要介绍WPF的数据绑定(Data Binding)功能,如果你已经熟悉本文的内容,可以跳过并直接阅读后面的文章。 什么是数据绑定 我们先来看一下MSDN上的说明: 数据绑定是在应用 UI 与其显示的数据之间建立连接的过程。 如…

关于Dell r730xd 老服务器的阵列卡 配置系统盘RAID 1

这里写自定义目录标题 关于Dell r730xd 老服务器的阵列卡 配置系统盘RAID 1操作步骤 关于Dell r730xd 老服务器的阵列卡 配置系统盘RAID 1 操作步骤 -开机后 按 Ctrl R 进入 RAID卡 配置界面,如下:-下面图片是 服务器中硬盘都已经准备好,并…

Qt Udp的组播(多播)、广播和单播

UDP通讯的基本概念和特点‌ UDP(User Datagram Protocol,用户数据报协议)是‌TCP/IP协议族中的一种无连接协议,主要用于那些对实时性要求较高而可靠性要求较低的应用场景。UDP的主要特点包括: ‌无连接‌:…

算法每日双题精讲——双指针(快乐数,盛最多水的容器)

🌟快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 🌟 别再犹豫了!快来订阅我们的算法每日双题精讲专栏,一起踏上算法学习的精彩之旅吧!💪…

C语言 | Leetcode C语言题解之第551题学生出勤记录I

题目&#xff1a; 题解&#xff1a; bool checkRecord(char* s) {int absents 0, lates 0;int n strlen(s);for (int i 0; i < n; i) {char c s[i];if (c A) {absents;if (absents > 2) {return false;}}if (c L) {lates;if (lates > 3) {return false;}} els…

【未解决】vite反向代理问题

文章目录 可行网页直接访问&#xff0c;数据正常返回不使用反向代理&#xff0c;直接用axios可以得到数据postman测试也正常 不行-vite反向代理出问题case1命令行测试 可行 网页直接访问&#xff0c;数据正常返回 在地址栏输入 https://api.binance.com/api/v3/ticker/price?…

github使用基础

要通过终端绑定GitHub账号并进行文件传输&#xff0c;你需要使用Git和SSH密钥来实现安全连接和操作。以下是一个基本流程&#xff1a; 设置GitHub和SSH 检查Git安装 通过终端输入以下命令查看是否安装Git&#xff1a; bash 复制代码 git --version配置Git用户名和邮箱 bash …

9_api_intro_imagerecognition_ocr2word

通用图片 OCR 到 Word API 数据接口 高可用图像识别引擎&#xff0c;基于机器学习&#xff0c;超精准识别率。 1. 产品功能 通用的识别接口&#xff0c; 支持多种图片格式&#xff1b;支持中英文字符混合识别&#xff1b;支持 Base64 以及网络地址传参&#xff1b;基于机器学习…

深度优先搜索之全排列问题(C语言版)

本文的一些参考&#xff1a; DFS (深度优先搜索) 算法详解 模板 例题&#xff0c;这一篇就够了_dfs算法-CSDN博客 首先把深度优先搜索算法的基本概论摆出来 深度优先搜索算法&#xff08;Depth First Search&#xff0c;简称DFS&#xff09;&#xff1a; 一种用于遍历或搜…

如何防止苹果MacOS进入休眠状态

前言 远程控制的时候&#xff0c;发现MacOS已经进入了休眠状态。如何设置MacOS&#xff0c;防止其进入休眠状态&#xff0c;这样才能远程控制。 1、进入系统偏好设置 显示器自动关闭了不要紧。只要操作系统不进入休眠就可以。

云计算:定义、类型及对企业的影响

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 云计算&#xff1a;定义、类型及对企业的影响 云计算&#xff1a;定义、类型及对企业的影响 云计算&#xff1a;定义、类型及对企…

Pr 视频过渡:沉浸式视频

效果面板/视频过渡/沉浸式视频 Video Transitions/Immersive Video Adobe Premiere Pro 的视频过渡效果中&#xff0c;沉浸式视频 Immersive Video效果组主要用于 VR 视频剪辑之间的过渡。 自动 VR 属性 Auto VR Properties是所有 VR 视频过渡效果的通用选项。 默认勾选&#x…

ArcGIS Pro SDK Addin-DAML

ArcGIS Pro SDK Addin-DAML 文章目录 ArcGIS Pro SDK Addin-DAML1 Panes: 重置窗格2 Button: 从功能区中移除核心按钮3 Button: 将新按钮插入功能区上的现有组4 Menu: 在图层上下文菜单中插入一个新按钮5 Menu: 在 Map Container 上下文菜单中插入新菜单6 Menu: 在2D Map上下文…