编码遗传学:JavaScript 继承之道

引入

JavaScript中的继承问题是一个很重要的知识点,很多面试都会问到。本文主要来详细地讲解JavaScript实现继承的不同方法。其中包括原型链继承、构造函数基础、组合式继承以及寄生组合式继承等ES5的实现方式,也会介绍ES6新出现的class继承

原型链继承

原型链继承简单地将子类的原型对象指向父类实例,这样子类实例在无法找到对应的属性或方法时会继续向其原型对象,即父类实例上查找,从而实现对父类属性和方法的继承。

function Person() {this.name = 'N-A';
}
Person.prototype.getName = function() {return this.name;
}
function Student() {}
Student.prototype = new Person();
// 原型的实例等于自身
Student.prototype.constructor = Student;
const student = new Student();
console.log(student.name); // N-A
console.log(student.getName()); // N-A

但这种方法存在一些缺陷:

1.引用类型共享:由于所有子类实例的原型都指向同一个父类实例,当子类实例修改继承的引用类型属性时,会影响所有子类实例,因为它们共享相同的原型对象。

function Person() {this.obj = {name: 'N-A',age: 5};
}
function Student() {}
Student.prototype = new Person();
// 原型的实例等于自身
Student.prototype.constructor = Student;
const student1 = new Student();
student1.obj.name = 'CSDN';
const student2 = new Student();
console.log(student2.obj.name); // CSDN

2.无法传参:在创建子类实例时无法向父类构造函数传递参数,因此无法像使用 super() 一样实现在子类中调用父类构造函数的功能,限制了灵活性和定制性。

构造函数继承

构造函数继承通过在子类构造函数中调用父类构造函数并使用子类的 this,将父类的成员属性和方法直接挂载到子类实例上。这种方式避免了子类实例共享一个原型实例,同时也能够向父类构造函数传递参数。

function Person(name) {this.name = name
}
Person.prototype.getName = function() {return this.name;
}
function Student() {Person.apply(this, arguments);
}const student = new Student('N-A');
console.log(student.name); // N-A

然而,这种继承方式同样也存在缺陷:无法继承父类原型上的属性和方法,子类只能继承到父类构造函数中定义的属性和方法,而无法直接访问和继承父类原型上的属性和方法。这导致子类无法复用父类原型链上的方法,降低了代码的复用性和灵活性。

虽然构造函数继承解决了一些原型链继承的问题,但它的局限性在于无法继承父类原型上的属性和方法,限制了子类的功能扩展和代码复用。

组合式继承

组合继承结合了原型链继承和构造函数继承的优点,通过在子类构造函数中调用父类构造函数来实现对父类属性的继承,同时利用原型链继承来继承父类原型上的方法和属性

function Person(name) {this.name = name;
}
Person.prototype.getName = function() {return this.name;
}
function Student() {// 构造函数继承Person.apply(this, arguments)
}
// 原型式继承
Student.prototype = new Person();// 原型的实例等于自身
Student.prototype.constructor = Student;const student = new Student('N-A');
console.log(student.name); // N-A
console.log(student.getName()); // N-A

但是,这种继承方式也存在一些缺陷:重复调用构造函数:每次创建子类实例时,都会执行两次构造函数。一次是通过 Person.apply 将父类的属性赋予子类实例,另一次是使用 new Person() 创建父类实例,并将其作为子类原型的一部分。这不影响对父类属性的继承,但会导致子类原型中存在两份相同的属性和方法,造成资源浪费和内存占用。

尽管组合继承解决了原型链继承和构造函数继承各自的缺陷,但重复调用构造函数会导致资源浪费,使得子类原型上存在冗余的属性和方法,不够优雅。

寄生式组合继承

寄生式组合继承是一种通过在子类构造函数中使用 Object.create 方法来优化组合继承的方式,解决了重复调用构造函数的问题。

function Person(name) {this.name = name;
}
Person.prototype.getName = function() {return this.name;
}
function Student() {// 构造函数继承Person.apply(this, arguments)
}
// 原型式继承
// Student.prototype = new Person();
Student.prototype = Object.create(Person.prototype);// 原型的实例等于自身
Student.prototype.constructor = Student;const student = new Student('N-A');
console.log(student.name); // N-A
console.log(student.getName()); // N-A

它的基本原理是:

1. 使用 Object.create 创建一个临时的中间对象,这个对象的原型指向了父类的原型对象,但不直接执行父类的构造函数。

2. 将这个临时创建的对象赋值给子类的原型,这样子类的原型就可以继承自父类的原型,但避免了多余的构造函数调用。

这种继承方式在 ES5 中被认为是相对成熟且高效的继承方式,解决了组合继承中构造函数被执行两次的问题,保留了原型链继承和构造函数继承的优点,同时避免了它们的缺点。

Class继承

ES6 引入了类的概念,可以使用 classextends 关键字来实现类的继承。

class Person {constructor(name) {this.name = name;}greet() {return `Hello, my name is ${this.name}.`;}
}class Student extends Person {constructor(name, level) {super(name); // 调用父类构造函数来初始化父类的属性this.level = level;}study() {return `${this.name} studies at level ${this.level}.`;}
}// 创建一个 Student 实例
const student = new Student('N-A', 'Senior');console.log(student.greet()); // 输出:Hello, my name is N-A.
console.log(student.study()); // 输出:N-A studies at level Senior.

在这个示例中:

Person 类有一个 name 属性和一个 greet 方法。Student 类通过 extends 关键字继承了 Person 类,并在构造函数中使用 super() 调用父类的构造函数初始化父类的属性。

Student 类还有一个 study 方法。创建 Student 类的实例,并调用继承自父类和自身定义的方法。

类继承使得 JavaScript 中的继承更加直观和易用,同时也更符合传统面向对象编程的习惯。通过 extends 关键字,子类可以轻松地继承父类的属性和方法,并且可以在子类中添加自己的属性和方法。使用 super() 可以方便地调用父类构造函数,初始化父类的属性。

好啦,本文就到这里啦!

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

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

相关文章

flutter调试器查看不了副页面(非主页面/子页面)

刚接触flutter,写了两个页面,通过按钮,可以从主页面跳转到副页面,副页面我自己写的一个独立的dart文件,在主页面的代码中导入使用。但是当我运行代码后,点击跳转的时候,却发现查看不到对应的副页…

nodejs微信小程序+python+PHP沧州地区空气质量数据分析系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

深度学习踩坑记录

深度学习踩坑记录 在跑一个深度学习的项目的时候,为了计算每个epoch的Loss,肯定要把每个batch_size的loss给加起来,就会有类似如下的代码 train_loss loss是的当时手抖,忘了是loss.item(),代码也能正常运行&#xf…

linux(centos7)离线安装mysql-5.7.35-1.el7.x86_64.rpm-bundle.tar

1. 卸载mariadb相关rpm # 查找 rpm -qa|grep mariadb rpm -qa|grep mysql# 卸载 rpm -e --nodeps mariadb... rpm -e --nodeps mysql...2. 删除mysql相关文件 # 查找 find / -name mysql# 删除 rm -rf /var/lib/mysql...3. 查看是否有相关依赖,没有需安装 rpm -q…

Android Studio的代码笔记--Adapter+GridView学习

AdapterGridView学习 AdapterGridViewSimpleAdapterGridViewactivity_main.xmlappicon.xmlMainActivity 自定义BaseAdapterGridView已下载应用PackageInfoAppAdapterMainActivity2 其他获取已下载应用信息函数获取所有应用信息函数ImageView产生圆角的方法背景设置很渐变设置选…

物联网架构之 Hadoop

修改/etc/hosts文件 192.168.107.197 node1 192.168.107.196 node2 192.168.107.195 node3 创建用户并加入组 groupadd hadoop useradd -g hadoop hduser passwd hduser vim /etc/sudoers hduser ALL(ALL) ALL 安装JDK rpm -ivh jdk-8u171-linux-x64.rpm vim /etc/profile e…

Hadoop学习总结(Hive的安装)

Hive的安装模式分为3种,分别是嵌入模式、本地模式、远程模式。 (1)嵌入模式:使用内嵌的 Derby 数据库存储元数据,这种方式是 Hive 的默认安装方式,配置简单,但是一次只能连接一个客户端&#xf…

MATLAB算法实战应用案例精讲-【图像处理】图像特征提取(附MATLAB代码实现)

目录 前言 知识储备 提取图像文本的Python 库 1. pytesseract 2. EasyOCR 3. Keras-OCR 4. TrOCR 5. docTR

模块一——双指针:18.四数之和

文章目录 题目描述算法原理排序双指针 代码实现排序双指针复杂度分析时间复杂度:O(N^3^)空间复杂度:O(log⁡N)或者O(N) 题目描述 题目链接:18.四数之和 算法原理 排序双指针 依次固定⼀个数a ;在这个数a 的后⾯区间上&#x…

C#实现支付宝转账功能

环境 .net 6 AlipaySDKNet.OpenAPI 2.4.0 申请证书 登录支付宝开放平台https://open.alipay.com/ 进入控制台 授权回调地址也设置一下,加密方式AES 新建.net 6空白的web项目 证书除了java都需要自己生成一下pkcs1的密钥 privatekey.txt就是根据应用私钥生成…

玩转大数据16:大数据存储与文件格式优化

随着大数据时代的到来,存储和处理海量数据成为了一个重要的挑战。在大数据存储中,选择合适的文件格式对数据的压缩率、读写性能和扩展性起着关键作用。本文将介绍大数据存储的挑战,探讨常见的文件格式,并深入讨论文件格式优化的策…

低代码开发:现实挑战与发展前景

低代码开发是近年来迅速崛起的软件开发方法,让编写应用程序变得更快、更简单。有人说它是美味的膳食,让开发过程高效而满足,但也有人质疑它是垃圾食品,缺乏定制性与深度。 一、什么是低代码 低代码开发是一种基于图形用户界面&…

小新Air-14 Plus 2021款AMD ACN版(82L7)原装出厂Win11系统镜像

LENOVO联想笔记本开箱状态原厂Windows11系统包 链接:https://pan.baidu.com/s/1D_sYCJAtOeUu9RbTIXgI3A?pwd96af 提取码:96af 联想小新AIR14笔记本电脑原厂系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 所需要工具&am…

Mapreduce小试牛刀(1)

1.与hdfs一样,mapreduce基于hadoop框架,所以我们首先要启动hadoop服务器 --------------------------------------------------------------------------------------------------------------------------------- 2.修改hadoop-env.sh位置JAVA_HOME配…

MBR30300FCT-ASEMI高耐压肖特基MBR30300FCT

编辑:ll MBR30300FCT-ASEMI高耐压肖特基MBR30300FCT 型号:MBR30200FCT 品牌:ASEMI 封装:TO-220F 最大平均正向电流:30A 最大重复峰值反向电压:300V 产品引线数量:3 产品内部芯片个数&…

***Cpolar配置外网访问和Dashy

Dashy是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航页。一款功能超强大,颜值爆表的可定制专属导航页工具 结合cpolar内网工具,我们实现无需部署到公网服务器…

android 13.0 Launcher3禁止拖动图标到Hotseat功能实现

1.概述 在13.0系统Launcher3进行定制化开发中,对于hotseat的开发中,由功能需求要求禁止拖动图标到Hotseat的功能,而拖拽也是在workspace.java中处理的 接下来就从workspace.java 开始找解决的办法 2.Launcher3禁止拖动图标到Hotseat相关代码分析 packages/apps/Launcher3/…

一文1000字基于Jenkins实现接口自动化持续集成!

一、JOB项目配置 1、添加描述 可选选项可填可不填 2、限制项目的运行节点 节点中要有运行环境所需的配置 节点配置教程:https://blog.csdn.net/YZL40514131/article/details/131504280 3、源码管理 需要将脚本推送到远程仓库中 4、构建触发器 可以选择定时构建…

ARM MMU简介

MMU内存保护场景 ARM MMU能够针对多种场景进行内存保护,包括以下几个常见的例子: 1. 操作系统级别的内存隔离 ARM MMU可以将不同的进程或线程的虚拟地址映射到不同的物理地址空间,实现进程间的内存隔离。这样可以确保一个进程无法访问或篡…

vite 打包图标icon ,content 内容乱码

问题描述:本地开发环境icon 显示正常,打包后发布线上环境icon乱码,而且具有偶发性,刷新页面乱码又正常了。 找问题:观察发现是content 内容没有编译成功导致乱码 解决:vite.config.ts / vite.config.js 文件…