〖大前端 - 基础入门三大核心之JS篇(57)〗- 继承

news2024/11/26 19:54:05
  • 说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费如需要项目实战或者是体系化资源,文末名片加V!
  • 作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司担任研发部门CTO。
  • 荣誉:2022年度博客之星Top4、2023年度超级个体得主、谷歌与亚马逊开发者大会特约speaker全栈领域优质创作者

  • 🏆 白宝书系列
    • 🏅 启示录 - 攻城狮的自我修养
    • 🏅 Python全栈白宝书
    • 🏅 ChatGPT实践指南白宝书
    • 🏅 产品思维训练白宝书
    • 🏅 全域运营实战白宝书
    • 🏅 大前端全栈架构白宝书


文章目录

  • ⭐ 继承
    • 🌟 什么是继承
    • 🌟 通过原型链实现继承
    • 🌟 借助构造函数
    • 🌟 组合继承
    • 🌟 原型式继承
    • 🌟 寄生式继承
    • 🌟 寄生组合式继承

⭐ 继承

在JavaScript中,继承是一种允许我们创建一个类(子类)从已有的类(父类)上继承所有的属性和方法的机制。这样的机制有助于我们复用和重用代码。

JavaScript原型链实现了继承。每一个对象都有一个内部属性[[prototype]],这个属性是一个链接/link(也就是一个指针),它指向了创建这个对象的函数的原型对象prototype。其实这就是“原型链”的起点,通过这个链子对象可以访问到父对象的属性。

在ES6之后,JavaScript又引入了基于类的继承,这种继承方式更接近于传统的面向对象语言,如Java,C++等。虽然JavaScript是基于原型的语言,但是为了更好地与其他面向对象编程语言交流和学习,ES6引入了class和extends关键字来实现基于类的继承。

但是无论是原型链继承还是ES6的类继承,其本质上都是原型继承,只是类继承更易于理解和使用。

🌟 什么是继承

先来看两个无关的类:PeopleVehicle,这两个类分别描述人类和机动车。People类含有的属性有:name(姓名)、age(年龄)、sex(性别),方法有:sayHello(打招呼)、sleep(睡觉);Vehicle类含有的属性有:brand(品牌)、color(颜色)、engineType(发动机型号)、seatingCapacity(座位容量),方法有:move(移动)、whistle(鸣笛);可以看出他们的属性和方法几乎没有重复和相关的,所以他们是两个无关的类:

image-20231107144610305

(上面两个图形是来源于UML的类的图形化表示法。UML,是一种统一建模语言,我们作为开发者能看懂图就可以了,不需要深入的了解UML的具体使用方式)

再来看两个有关的类:PeopleStudent,这两个类分别描述人类和学生。Student类含有的属性有:name(姓名)、age(年龄)、sex(性别)、studentNumber(学号)、school(学校),方法有:sayHello(打招呼)、sleep(睡觉)、study(学习)、exam(考试)。image-20231107144525802

我们可以发现:

  • People类拥有的属性和方法Student类都有,并且Student类还扩展了一些属性和方法
  • Student是一种People,两类之间是“is a kind of”关系

这就是**“继承”**关系:Student类继承自People类

People是**“父类”(或“超类”、“基类”);Student是“子类”**(或”派生类“)

子类丰富了父类,让类描述的更具体,更细化

在UML中,用空心的箭头来描述继承关系,箭头指向父类:

image-20231107145354213

更多继承关系的举例:

父类子类
PeopleStudent、Teacher
Vehicle(机动车)Car(小轿车)、Truck(卡车)、Motocycle(摩托车)
Applicance(家用电器)Television(电视)、Refrigerator(冰箱)
Publication(出版物)Book(书籍)、Magazine(杂志)

往往一个类只是一个继承”链“中的一环,比如子类也可以有自己的子类,父类也会有自己的父类。所以面向对象方法实际上是模仿了自然界中描述自然事物的方法,这样就使面向对象的编程方式非常容易被理解。

JavaScript 中如何实现继承?

实现继承的关键在于:子类必须拥有父类的全部属性和方法,同时子类还应该能定义自己特有的属性和方法

使用JavaScript特有的原型链特性来实现继承,是普遍的做法

🌟 通过原型链实现继承

首先定义一个构造函数Peolpe,我们可以在People.prototype上定义一些人类的方法(比如sayHello()sleep()等)然后new出来一个People的实例。然后让Student构造函数的prototype直接指向People的实例(关键步骤),我们把study()方法,和exam()方法直接定义到People的实例上。这样的巧妙之处在于如果我们new出来一个student(如Hanmeimei),就可以行成了一个原型链,Hanmeimei可以调用study()exam()方法,也可以调用sayHello()sleep()方法。

image-20231107151615507

下面来敲一些这个demo:

// 父类
function People(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}

People.prototype.sayHello = function () {
    console.log("你好,我是" + this.name + "我今年" + this.age + "岁了");
}
People.prototype.sleep = function () {
    console.log("我要睡觉了,zzzzzz");
}

// 子类
function Student(name, age, sex, school, studentNumber) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.school = school;
    this.studentNumber = studentNumber;
}
// 关键语句,实现继承 这个语句一定要写在这个位置(位于定义子类语句和增加子类原型方法的中间)
Student.prototype = new People();  // 将子类的prototype指向父类的一个实例

Student.prototype.study = function () {
    console.log(this.name + '正在学习');
}
Student.prototype.exam = function () {
    console.log(this.name + '正在考试。。。');
}

var hanmeimei = new Student('韩梅梅', 12, '女', '实验小学', '10001');
hanmeimei.study();
hanmeimei.sayHello();

image-20231107153943349

子类也可以重写父类的一些原型方法,比如我们将上述案例中的sayHello()方法在Student中重写

demo如下:

// 子类中重写sayHello()方法
Student.prototype.sayHello = function () {
    console.log('敬礼!您好,我是' + this.name + ",我今年" + this.age + '岁了');
}

image-20231107154625140

综上,我们可以总结出面向对象编程的特质如下(重点,一定要理解):

  • 面向对象的本质:定义不同的类,让类的实例工作
  • 面向对象的优点:程序编写更清晰、代码结构更严密、使代码更健壮更利于维护
  • 面向对象经常用到的场合:需要封装和复用性的场合(组合性思维)

🌟 借助构造函数

为了解决原型中包含引用类型值所带来问题和子类构造函数不优雅的问题,开发人员通常使用一种叫做”借助构造函数“的技术,也被称为”伪造对象“或”经典继承

借助构造函数的思想非常简单:在子类构造函数的内部调用超类的构造函数,但是要注意使用call()绑定上下文

优雅的在子类中使用父类的属性和方法:

function People(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.arr = [11, 22, 33];   // 引用类型值,父类的引用类型值会写到实例的身上
}
function Student(name, sex, age, school, sid) {
    People.call(this, name, sex, age);   // 使用call绑定上下文
    this.school = school;
    this.sid = sid;
}
Student.prototype = new People();

var xiaoming = new Student('小明', '男', '10', '实验小学', '100001');
xiaoming.arr.push('66')
console.log(xiaoming);

var xiaohong = new Student('小红', '女', '11', '实验小学', '100002');
console.log(xiaohong);

image-20231114133457727

🌟 组合继承

将通过原型链和借用构造函数的技术组合到一起,叫做组合继承,也叫做伪经典继承

组合继承是JavaScript中最常用的继承方式

示例代码:

// 父类
function People(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
People.prototype.sayHello = function () {
    console.log("你好,我是" + this.name + ",我今年" + this.age + "岁了");
}
People.prototype.sleep = function name(params) {
    console.log("我要睡觉了,zzzzzz");
}
// 子类
function Student(name, sex, age, school, sid) {
    // 借助构造函数
    People.call(this, name, sex, age);
    this.school = school;
    this.sid = sid;
}
// 实现继承,通过原型链
Student.prototype = new People();
Student.prototype.exam = function () {
    console.log(this.name + '正在考试。。。');
}

var xiaoming = new Student('小明', '男', '10', '实验小学', '100001');
xiaoming.sayHello();
xiaoming.sleep();
xiaoming.exam();

image-20231114134835929

组合继承的缺点:组合继承最大的问题就是无论什么情况下,都会调用两次父类的构造函数,一次是在创建子类原型的时候,另一次是在子类构造函数的内部,这就会造成效率的浪费。

虽说组合继承有缺点,但无伤大雅,依然是JS中最常用的继承方式,一定要掌握这种继承的技巧。

🌟 原型式继承

在没有必要“兴师动众”地创建构造函数,而只是想让新对象与现有对象“类似”的情况下,使用Object.create()即可胜任,称为原型式继承

  • 认识Object.create()方法
    • IE9+开始支持Object.create()方法,可以根据指定的对象为原型创建出新对象
    • Object.create()让我们可以不借助任何构造函数就让一个对象的__proto__指向另一个对象

image-20231114141357812

Object.create() 还支持再传入一个参数,用于补充或重写新的属性,示例代码:

var obj1 = {
    a: 11,
    b: 22,
    c: 33,
    test: function () {
        console.log(this.a + this.b);
    }
};

var obj2 = Object.create(obj1, {
    d: {
        value: 44
    },
    a: {
        value: 111
    }
})

console.log(obj2.__proto__ === obj1);
console.log(obj2.a);    // 111,因为重写了obj2的a属性,就会把从obj1继承的属性值覆盖
console.log(obj2.b);    // 22
console.log(obj2.c);    // 33
console.log(obj2.d);    // 44

obj2.test();        // 133  (111+22=133)

image-20231114155452548

Object.create()的兼容性写法----面试常考

如何在低版本浏览器实现Object.create()方法呢?示例代码:

// 道格拉斯•克洛克福德写的一个函数,非常巧妙,面试常考
// 函数的功能就是以o为原型,创建新对象
function object(o) {
    // 创建一个临时构造函数
    function F() { }
    // 让这个临时构造函数的prototype指向o,这样一来它new出来的对象,__proto__就指向了o
    F.prototype = o;
    // 返回F的实例
    return new F();
}

var obj1 = {
    a: 11,
    b: 22
}
var obj2 = object(obj1);

console.log(obj2.__proto__ === obj1);
console.log(obj2.a);
console.log(obj2.b);

image-20231114160909468

🌟 寄生式继承

寄生式继承:编写一个函数,它接收一个参数o,返回以o为原型的新对象p,同时给p上添加预置的新方法

寄生式继承仰赖一个函数,对象o需要传给这个函数,这个函数内部要创建一个新对象,这个新对象的__proto__指向o,函数内部对o进行“加工”,然后再输出一个新对象p,新对象上添加了一些预置的新方法

新的对象感觉像是“寄生”在原来的对象上面,所以叫做“寄生”式继承

image-20231115094709771

示例代码:

var obj1 = {
    a: 11,
    b: 22
}
var obj2 = object(obj1);

console.log(obj2.__proto__ === obj1);
console.log(obj2.a);
console.log(obj2.b);



var o1 = {
    name: '小明',
    age: 12,
    sex: '男'
}
var o2 = {
    name: '小红',
    age: 11,
    sex: '女'
}

// 寄生式继承
function f(o) {
    // 以o为原型创建出新对象
    var p = Object.create(o);
    // 补充新方法
    p.sayHello = function () {
        console.log("你好,我是" + this.name + ",我今年" + this.age + "岁了");
    }
    // 补充新方法
    p.sleep = function () {
        console.log("我要睡觉了,zzzzzz");
    }
    return p;
}

var p1 = f(o1);
p1.sayHello();

var p2 = f(o2);
p2.sayHello();

image-20231115133357009

总结:

  • 寄生式继承就是编写一个函数,它可以“增强对象”,只要把对象传入这个函数,这个函数将以此对象为“基础”创建出新对象,并为新对象赋予新的预置方法
  • 在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式
  • 和原型式继承相比,寄生式继承封装性更强,就像一个小工厂,可以对一个对象进行加工,产生一个新的对象

寄生式继承的缺点:

  • 使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,即“方法没有写到prototype上”

🌟 寄生组合式继承

之前提到的组合继承最大的问题就是无论什么情况下,都会调用两次超类的构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数的内部。那么如何可以避免这个问题呢,利用寄生组合式继承就可以避免调用两次超类的构造函数问题。

寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式继承方法

寄生组合式继承背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已 。本质上,就是 使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型

image-20231115151214742

示例代码:

// 父类
function People(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
People.prototype.sayHello = function () {
    console.log("你好,我是" + this.name + ",我今年" + this.age + "岁了");
}
People.prototype.sleep = function name(params) {
    console.log("我要睡觉了,zzzzzz");
}
// 子类
function Student(name, sex, age, school, sid) {
    // 借助构造函数
    People.call(this, name, sex, age);
    this.school = school;
    this.sid = sid;
}

// 调用我们自己编写的inheritPrototype函数,
// 这个函数可以让Student类的prototype指向以“People.prototype为原型的一个新对象”
inheritPorpotype(Student, People);

// 子类中重写sayHello()方法
Student.prototype.sayHello = function () {
    console.log('敬礼!您好,我是' + this.name + ",我今年" + this.age + '岁了');
}
Student.prototype.exam = function () {
    console.log(this.name + '正在考试。。。');
}


var xiaoming = new Student('小明', '男', '10', '实验小学', '100001');
xiaoming.sayHello();
xiaoming.sleep();
xiaoming.exam();

image-20231115152643466

instanceof运算符:instanceof运算符用来检测“某个对象是不是某个类的”

总结

继承已经学习完毕了,下面我们来总结一下继承的几种方式:

image-20231115153502063

其中组合继承是JavaScript中最常用的继承方式,一定要熟悉并掌握这几种继承方式,这些都是我们开发过程中常用的编码技巧。

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

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

相关文章

Leetcode—11.盛最多水的容器【中等】

2023每日刷题&#xff08;六十三&#xff09; Leetcode—11.盛最多水的容器 实现代码 #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) int maxArea(int* height, int heightSize) {int left 0, right heightSize - 1;int m…

【具身智能评估7】ProcTHOR: Large-Scale Embodied AI Using Procedural Generation

论文标题&#xff1a;ProcTHOR: Large-Scale Embodied AI Using Procedural Generation 论文作者&#xff1a;Matt Deitke, Eli VanderBilt, Alvaro Herrasti, Luca Weihs, Jordi Salvador, Kiana Ehsani, Winson Han, Eric Kolve, Ali Farhadi, Aniruddha Kembhavi, Roozbeh M…

118. 杨辉三角

描述 : 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 题目 : LeetCode 118. 杨辉三角 : 118. 杨辉三角 分析 : 这道题用二维数组来做 . 解析 : class Solution {public Li…

python图像二值化处理

目录 1、双峰法 2、P参数法 3、迭代法 4、OTSU法 图像的二值化处理是将图像上的像素点的灰度值设置为0或255&#xff0c;也就是将整个图像呈现出明显的只有黑和白的视觉效果。二值化是图像分割的一种最简单的方法&#xff0c;可以把灰度图像转换成二值图像。具体实现是将大…

OpenSSL 3.2.0新增Argon2支持——防GPU暴力攻击

1. 引言 OpenSSL新发布的3.20版本中&#xff0c;引入了一些新特性&#xff0c;包括&#xff1a; post-quantum方法Brainpool曲线QUICArgon2&#xff1a;Argon2 是一种慢哈希函数&#xff0c;在 2015 年获得 Password Hashing Competition 冠军&#xff0c;利用大量内存计算抵…

Redis一些常用的技术

文章目录 第1关&#xff1a;Redis 事务与锁机制第2关&#xff1a;流水线第3关&#xff1a;发布订阅第4关&#xff1a;超时命令第5关&#xff1a;使用Lua语言 第1关&#xff1a;Redis 事务与锁机制 编程要求 根据提示&#xff0c;在右侧编辑器Begin-End补充代码&#xff0c;根据…

vuepress-----25、右侧目录

# 25、vuepress 右侧目录 https://github.com/xuek9900/vuepress-plugin-right-anchor vuepress-plugin-right-anchor English &#xff5c;中文 在用 Vuepress 2.x 编写的文档页面右侧添加 锚点导航栏 # 版本 2.x.x -> Vuepress 2.x -> npm next -> master 分支0…

c语言突击

一&#xff1a;前序 1.一个c语言程序有且只有一个main函数&#xff0c;是程序运行的起点&#xff01; 每个c语言程序写完后&#xff0c;都是先编译&#xff08;.c---&#xff09;&#xff0c;后链接&#xff08;.obj---&#xff09;&#xff0c;最后运行&#xff08;.exe&…

ROS-分布式通信

ROS是一个分布式计算环境。一个运行中的ROS系统可以包含分布在多台计算机上多个节点。根据系统的配置方式&#xff0c;任何节点可能随时需要与任何其他节点进行通信。 因此&#xff0c;ROS对网络配置有某些要求&#xff1a; 所有端口上的所有机器之间必须有完整的双向连接。每…

【VScode】设置语言为中文

1、下载安装好vscode 2、此时可看到页面为英文&#xff0c;为方便使用可切换为中文 3、键盘按下 ctrlshiftP 4、在输入框内输入configure display language 5、选择中文&#xff0c;restart即可&#xff08;首次会有install安装过程&#xff0c;等待安装成功后重启即可&am…

反爬虫介绍及其处理方法

反爬虫机制 封IP&#xff1a;监控短时间内同一地址的请求次数过大登录及验证码&#xff1a;对于监控后封IP之后短时间内继续的大量请求&#xff0c;要求登陆或验证码通过验证之后才能继续进行。健全账号体制&#xff1a;即核心数据只能通过账号登录后才能进行访问。动态加载数…

【.NET】控制台应用程序的各种交互玩法

关于控制台交互&#xff0c;大伙伴们也许见得最多的是进度条&#xff0c;就是输出一行但末尾不加 \n&#xff0c;而是用 \r 回到行首&#xff0c;然后输出新的内容&#xff0c;这样就做出进度条了。不过这种方法永远只能修改最后一行文本。 于是&#xff0c;有人想出了第二种方…

【MATLAB】数据拟合第12期-基于高斯核回归的拟合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 基于高斯核回归的拟合算法是一种处理回归问题的机器学习方法。以下是该算法的简单介绍&#xff1a; 核心思想&#xff1a;高斯核回归的核心思想是利用高斯核函数对数据点进行非线性映射&a…

AudioGPT 语音技术全覆盖:语音识别、增强、分离、风格迁移等 | 开源日报 No.114

stevearc/oil.nvim Stars: 1.7k License: MIT oil.nvim 是一个类似于 vim-vinegar 的文件浏览器&#xff0c;允许您像普通 Neovim 缓冲区一样编辑文件系统。其主要功能包括支持常见插件管理器、通过适配器抽象进行所有文件系统交互以及提供 API 来执行各种操作。该项目的关键…

RNN梯度爆炸实验

前情回顾 from torch.utils.data import Dataset import torch.nn as nn import torch.nn.functional as F import os import random import torch from nndl import Accuracy from nndl import RunnerV3 from torch.utils.data import DataLoader import matplotlib.pyplot a…

零基础自学C语言|字符函数和字符串函数

在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了方便操作字符和字符串&#xff0c;C语言标准库中提供了一系列库函数&#xff0c;接下来我们就学习一下这些函数。 &#x1f4cc;字符分类函数 C语言中有一系列的函数是专门做字符分类的&#xff0c;也就…

06_Web框架之Django三

Web框架之Django三 学习目标和内容 1、能够通过ORM模型创建数据表 2、能够通过ORM模型对数据进行操作 3、能够理解ORM模型对应关系 一、ORM概念 1、ORM介绍 对象关系映射 用于实现面向对象编程语言里不同类型系统数据之间的转换。 其就是使用面向对象的方式&#xff0c;操作…

风速预测(六)基于Pytorch的EMD-CNN-GRU并行模型

目录 前言 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为96&#xff0c;制作数据集 3 基于Pytorch的EMD-CNN-GRU并行模型预测 3.1 数据加载&a…

得帆信息创始人-张桐,受邀出席 BV百度风投AIGC主题论坛

近日&#xff0c;得帆信息创始人兼CEO张桐&#xff0c;作为百度风投被投代表企业创始人受邀出席“向未来&#xff0c;共成长” BV百度风投AIGC主题论坛。 与包括上海市徐汇区相关部门领导、百度集团相关事业部负责人及代表&#xff0c;以及来自国寿资本、中网投、麦顿投资的投资…

IDEA运行JSP启动后页面中文乱码

源代码截图&#xff1a; 运行结果截图&#xff1a; 在<head>标签内加入代码 <% page contentType"text/html; charsetgb2312"%> 重启服务器&#xff0c;问题已改善 ————————————————— 该文仅供学习以及参考&#xff0c;可做笔记收藏…