javascript 7种继承-- class继承(7)

news2024/11/14 3:00:24

文章目录

    • 概要
    • 继承的进化史
    • class继承
      • 1. 类声明与严格模式
      • 2. 类的实现
      • 3. 类的静态方法
      • 4. get,set 存取器
      • 5. 类中的公有继承以及私有继承
      • 6. 使用 super 调用超类
      • 7. Mix-ins / 混入
    • 源码: 类的继承
    • 效果图
    • 小结

概要

这阵子在整理JS的7种继承方式,发现很多文章跟视频,讲解后都不能让自己理解清晰,索性自己记录一下,希望个位发表需要修改的意见,共勉之。
部分文章总结出来的数据可能是错误的,网络上太多数据了,有些不严谨,当然我也可能是其中一人,如果有问题,欢迎提出来,共同进步。

继承的进化史

JS的7种并不是逐级进化的(个人觉得~),可以参考下图:

在这里插入图片描述

class继承

JS一开始的初衷就是为了方便开发者,当初的开发也比较简单就是单纯的页面,随着浏览器不断地发展,现在功能越来越多了,所以,才有7种继承的进化史。

class 是es6新增的特性,避免了开发者去使用繁杂的原型链继承构造函数继承组合式继承原型式继承寄生式继承寄生组合式继承
比如本人,一上来就用class…无奈面试官可不理会你这些哈哈:)

mdn参考文献:此处 文献中的概念跟大学学习C++的类,有点不一样~~~

JS一开始设计就是开发一些简单的页面,本文尽量按照面向对象的类来讲解,因为不知道哪天ES又升级了呢?


由于篇幅过长,就拿几个重点来讲解

1. 类声明与严格模式

  • 函数声明类声明之间的一个重要区别在于,函数声明会提升,类声明不会。你首先需要声明你的类,然后再访问它,否则类似以下的代码将抛出ReferenceError:
  • 即使你未设置 “use strict”class 体内部的代码总是在严格模式下执行,不能重复声明重复定义。
let person = new Parent(); // error~ 未声明 ReferenceError
class Parent(){}
...
class Parent(){}  // error~ 重复定义

2. 类的实现

一个类的类体是一对花括号/大括号 {} 中的部分。这是你定义类成员的位置,如方法或构造函数。
这里大概了解一下,类的类体中有什么内容,下文再继续讲解:

  • 构造函数
  • get,set 存取器
  • 方法
  • 静态方法
class Rectangle {
    // constructor
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
    // Getter
    get area() {
        return this.calcArea()
    }
    // Method
    calcArea() {
        return this.height * this.width;
    }
    // Static 下面有
}
const square = new Rectangle(10, 10);

console.log(square.area);
// 100

3. 类的静态方法

类的静态方法,存放在类中,而不是实例后的对象
static 关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化 (en-US) 该类,但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    static displayName = "Point";

	// 用来计算Point实例化后的2个对象之间的距离
    static distance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;
        return Math.hypot(dx, dy);
    }
}

const p1 = new Point(5, 5);
const p2 = new Point(10,10);
p1.displayName;
// undefined
p1.distance;
// undefined

console.log(Point.displayName);
// "Point"
console.log(Point.distance(p1, p2));
// 7.0710678118654755

4. get,set 存取器

class Parent {
  constructor(name) {
    console.warn("Parent 实例化了...");
    this._name = name;
  }
  
  sayName() {
    console.log("我的名字:", this._name);
  }

  get name() {
    return this._name;
  }

  set name(name) {
    this._name = name;
  }
}

let p = new Parent("钟先生", 31);
console.log("原先的名字:", p.name);
p.name = "钟先生超级帅";
console.log("通过class set 修改后的名字:", p.name);

5. 类中的公有继承以及私有继承

关于类这个概念本文章就不过多描述,一个是篇幅有限,一个是本人也没研究透彻,类是前人提出的概念,划分出3种继承

  • 公有继承:公有继承是最普遍且默认的继承方式,它在派生类中派生出的成员变量和成员函数都是公有的,可以被派生类的对象使用和访问。公有继承方式体现了“is-a”的关系,即派生类是基类的一种扩展或特化。在JS说人话就是可以通过对象 .操作符进行操作。
  • 保护继承:保护继承是介于公有继承和私有继承之间的一种继承方式。它在派生类中派生出的成员变量和成员函数都是保护的,不能被派生类的对象直接访问,但是可以在派生类的内部和派生类的子类中进行访问。保护继承方式体现了“derived-from”的关系,即派生类是基类的派生和扩展,但不希望外部使用派生类中的成员。JS应用暂时不知道。。。
  • 私有继承:私有继承是一种较为特殊的继承方式,它在派生类中派生出的成员变量和成员函数都是私有的,不能被派生类的对象直接访问,只能在派生类的内部进行访问和调用。私有继承体现了“实现的细节隐藏”的原则,只有派生类的内部可以直接访问和调用被继承类中的私有成员。在JS说人话就是不能通过.操作符直接修改,只能通过实例对应的类方法跟get/set存取器进行修改

小插曲:
JavaScript有一个非强制的但很有用的编程惯例,就是对所以私有变量或函数名加一个下划线(_)作为前缀,以标识他们是私有的,仅用于提醒开发人员。

该操作是非强制性的,对于一些实例对象的属性名还是可以通过.操作符修改。
在这里插入图片描述
这里只是标识作用,用于提醒开发者,并不能限制开发者直接修改其属性。

但是class继承可以划分出,公有属性以及私有属性

公有属性
在这里插入图片描述
可以通过对象.操作符修改,也可以通过类方法以及get、set存取器操作。

私有属性:使用私有字段,可以按以下方式细化定义。
在这里插入图片描述
仅仅可以通过类方法以及get、set存取器操作。

6. 使用 super 调用超类

super 关键字用于调用对象的父对象上的函数。

class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}

// 打印2条数据

// 比如 threejs 中就有过这段代码

在这里插入图片描述

7. Mix-ins / 混入

抽象子类或者 mix-ins 是类的模板。一个 ECMAScript 类只能有一个单超类,所以想要从工具类来多重继承的行为是不可能的。子类继承的只能是父类提供的功能性。因此,例如,从工具类的多重继承是不可能的。该功能必须由超类提供。

一个以超类作为输入的函数和一个继承该超类的子类作为输出可以用于在 ECMAScript 中实现混合:

有点类似寄生模式,通过工厂模式,扩展出你的 target 类。

var calculatorMixin = (Base) =>
  class extends Base {
    calc() {}
  };

var randomizerMixin = (Base) =>
  class extends Base {
    randomize() {}
  };

使用 mix-ins 的类可以像下面这样写:

class Foo {}
class Bar extends calculatorMixin(randomizerMixin(Foo)) {}

在这里插入图片描述

源码: 类的继承

类的继承,主要通过关键字extends
这边弄的稍微复杂了,但是没办法,东西很多,很多情况要对比,还没有跟寄生组合式继承对比呢~~~

  • 父类:Parent,要被继承的类,可以理解为人。
  • 子类:Children,要继承父类的类,可以理解为不同的工种,但是还是一个人,所以继承了父类的一些基础属性。
  • 实例:person1 ,person2,实例化的子类,一般实例化后数据就分开了。

父类属性:

  1. 构造函数中的 _name:公共属性,每个人都有名字 基础数据类型,这里加上_,指示是私有变量,告诉开发人员不要随意修改,但是还是可以修改的…
  2. 构造函数中的方法 syaName1:公共方法,实例化后放在了对象上了
  3. 类中的方法sayName:公共方法,实例化后放在了原型对象上了
  4. get、set存取器 name:class 的存储器,用于设置跟读取类中的属性的方法,不可以跟其他属性重名,这里用来修改_name
  5. 静态方法 static compareNmae:在类中的方法,实例对象没有的,可以通过Parent.compareNmae()调用,一般是对比实例对象用的。
    在这里插入图片描述

子类属性:

  1. #job:私有变量,只能构造函数跟get、set存取器才能修改。
  2. sayJob:子类中公共方法,实例化后放在了原型对象上了,私有变量,类中方法可以操作。
  3. get、set存取器 job:私有变量,存取器可以操作。
    在这里插入图片描述
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue路由中history模式的实现</title>
  </head>
  <body>
    <h1>Vue路由中history模式的实现</h1>
    <button id="myBtn">改变url</button>
    <script>
      class Parent {
        constructor(name) {
          // 公共属性,可以通过 .操作符修改
          this._name = name;
          // 放在了实例上
          this.sayName1 = function () {
            console.log(`我的名字1是:${this._name}`);
          };
        }

        // 公共方法-放在原型链上
        sayName() {
          console.log(`我的名字是:${this._name}`);
        }

        get name() {
          return this._name;
        }

        // 当然类中内部修改也是可以的
        set name(name) {
          this._name = name;
        }

        // 静态方法,一般用于比较2个实例,这里比较谁的名字长
        static compareName(a, b) {
          console.log(`${a.name}, 名字长度为${a.name.length}`);
          console.log(`${b.name}, 名字长度为${b.name.length}`);
          console.log(
            (a.name.length > b.name.length ? a.name : b.name) + "  的名字长"
          );
          return a.name.length > b.name.length;
        }
      }

      console.log("=========父类=========");
      let p = new Parent("Penk");
      p.sayName();
      p.name = "Penk是个码农";
      p.sayName();

      class Children extends Parent {
        // 私有变量
        // 1. 构造函数
        // 2. 内部方法或者get、set读取器
        #job = "没工作";
        // 构造函数
        constructor(name, job) {
          super(name);
          this.#job = job;
        }

        // 内部方法
        sayJob() {
          console.log(`我叫${this.name},我的工作是:${this.#job}`);
        }

        // 读取器
        get job() {
          return this.#job;
        }

        set job(j) {
          this.#job = j;
        }
      }

      console.log("=========子类=========");
      let person = new Children("钟先生", "程序员");
      person.sayName();
      person.name = "钟先生超级帅";
      person.sayName();
      // 直接修改,因为是公共属性
      person._name = "钟先生";
      person.sayName();

      person.sayJob();
      person.job = "扫地大叔1111111";
      person.sayJob();
      // 无效,根本读不到该值...
      person["#job"] = "程序员";
      person.sayJob();

      // 静态方法比较长度
      let person2 = new Children("刘备", "主公");
      Parent.compareName(person, person2);

      console.log(p);
      console.log(person);
    </script>
  </body>
</html>

效果图

父类实例化对象,通过class 存取器修改_name属性

console.log("=========父类=========");
let p = new Parent("Penk");
p.sayName();
p.name = "Penk是个码农";
p.sayName();

在这里插入图片描述


子类,实例化对象,通过存取器修改,也通过了对象属性直接修改。

console.log("=========子类=========");
let person = new Children("钟先生", "程序员");
person.sayName();
person.name = "钟先生超级帅";
person.sayName();
// 直接修改,因为是公共属性
person._name = "钟先生";
person.sayName();

在这里插入图片描述

这边重复打印数据,是因为子类重写的sayName方法中,通过super 调用了父类的sayName

在这里插入图片描述


打印了子类的sayJob方法,因为#job是私有属性,只能通过读取器job进行修改:

person.sayJob();
person.job = "扫地大叔1111111";
person.sayJob();
// 无效,根本读不到该值...
person["#job"] = "程序员";
person.sayJob();

在这里插入图片描述


调用了类中的静态方法,静态方法一般不是用于对对象的操作,这边的功能是比较2个名字的长短,虽然有点不实用…

// 静态方法比较长度
let person2 = new Children("刘备", "主公");
Parent.compareName(person, person2);

在这里插入图片描述

小结

class 不仅仅是简化了之前讲解的6种继承方式,也更加严谨,提供了更多的更像面向对象变成的方法(读取器,静态方法等)。但是不同的项目需要进行技术调研,class或许不是最优的解决方案,但一定是最优雅的。

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

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

相关文章

【2023 华数杯全国大学生数学建模竞赛】 B题 不透明制品最优配色方案设计 详细建模方案解析及参考文献

【2023 华数杯全国大学生数学建模竞赛】 B题 不透明制品最优配色方案设计 详细建模方案解析及参考文献 1 题目 B 题 不透明制品最优配色方案设计 日常生活中五彩缤纷的不透明有色制品是由着色剂染色而成。因此&#xff0c;不透明制品的配色对其外观美观度和市场竞争力起着重要…

GD32F103VE定时器0

本测试程序&#xff0c;配置GD32F103VE定时器0每500ms中断一次&#xff0c;中断时&#xff0c;开关LED灯。 只讲定时器&#xff0c;多了&#xff0c;有点乱。有的人喜欢汇总&#xff0c;Timer的功能太多&#xff0c;放在一起&#xff0c;会搞混&#xff0c;不好移植。即使放一…

【雕爷学编程】MicroPython动手做(31)——物联网之Easy IoT 2

1、物联网的诞生 美国计算机巨头微软(Microsoft)创办人、世界首富比尔盖茨&#xff0c;在1995年出版的《未来之路》一书中&#xff0c;提及“物物互联”。1998年麻省理工学院提出&#xff0c;当时被称作EPC系统的物联网构想。2005年11月&#xff0c;国际电信联盟发布《ITU互联网…

哪些情况下需要使用爬虫IP

不知道小伙伴们有没有遇到过这种场景&#xff1a;上网闲逛&#xff0c;看一些搞笑的视频或者想下载一些酷炫的文件&#xff0c;正点击呢&#xff0c;结果却发现被网站限制了&#xff0c;无法访问或者下载&#xff1f; 别急&#xff0c;今天我来告诉大家&#xff0c;如何借助使…

IE浏览器,和Edge浏览器

目录 一.IE浏览器&#xff08;前世今生&#xff09; 1.什么是IE浏览器&#xff1f; 2.IE浏览器发展历程 3.IE浏览器在早些年为什么这么流行 4.ie浏览器为什么被停用? 5.IE浏览器无法适应如今的Web发展原因 二.Edge&#xff08;发展&#xff09; 1.什么是Edge浏览器&…

2023年人工智能技术与智慧城市发展白皮书

人工智能与智慧城市是当前热门的话题和概念&#xff0c;通过将人工智能技术应用在城市管理和服务中&#xff0c;利用自动化、智能化和数据化的方式提高城市运行效率和人民生活质量&#xff0c;最终实现城市发展的智慧化&#xff0c;提升城市居民的幸福感。 AI技术在城市中的应…

【修正-高斯拉普拉斯滤波器-用于平滑和去噪】基于修正高斯滤波拉普拉斯地震到达时间自动检测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

iMX6ULL应用移植 | 移植 infoNES 模拟器(重玩经典NES游戏)

没玩过NES游戏的童年&#xff0c;可能不是80后的童年。我们小时候是从玩FC开始接触游戏机的&#xff0c;那时真的是红极一时啊&#xff0c;我上初中时还省吃俭用买了一台小霸王&#xff0c;暑假里把电视机都给打爆了&#xff01;那时任天堂单是FC机的主机的发售收入就超过全美的…

小白解密ChatGPT大模型训练;Meta开源生成式AI工具AudioCraft

&#x1f989; AI新闻 &#x1f680; Meta开源生成式AI工具AudioCraft&#xff0c;帮助用户创作音乐和音频 摘要&#xff1a;美国公司Meta开源了一款名为AudioCraft的生成式AI工具&#xff0c;可以通过文本提示生成音乐和音频。该工具包含三个核心组件&#xff1a;MusicGen用…

Spring源码面试题

Spring源码面试题 谈谈你对Spring框架的理解? Spring 是一个开源的应用程序框架&#xff0c;它起源于 Rod Johnson 在其著名的 Spring Framework 专著中提出的一个轻量级框架的观念。下面是 Spring 的发展历史&#xff1a; 2002 年&#xff0c;Rod Johnson 发表了他的专著 …

GPT Prompt编写的艺术:如何提高AI模型的表现力

随着AI技术的迅速发展&#xff0c;人工智能模型变得越来越强大&#xff0c;能够协助我们完成各种任务。然而&#xff0c;如何更好地利用AI的能力仍然存在很大的探索空间。在与AI进行交互的过程中&#xff0c;我们主要依赖于Prompt&#xff0c;不管是直接与大模型交互&#xff0…

dlib的安装

由于需要人脸识别&#xff0c;所以需要安装opencv和dlib&#xff0c;OpenCV的安装很顺利&#xff0c;实例也跑的很正常。但dlib的安装却出现了很多坑&#xff0c;而且国内的解决方法都是复制粘贴&#xff0c;一点营养都没有&#xff0c;查了国外资料&#xff0c;终于解决&#…

让Python点亮你的世界:打造专业级编程环境的必备步骤

文章目录 初识pythonpython的安装win系统Linux系统&#xff08;centos7&#xff09; 第一个Python程序常见问题 Python解释器Python开发环境PyCharm的基础使用创建项目修改主题修改默认字体和大小汉化插件翻译软件常用快捷键 初识python Python语言的起源可以追溯到1989年&…

OFCMS代码审计

环境搭建 https://blog.csdn.net/oufua/article/details/82584637 安装后是重启容器 最后 db-config.properties 改成db.properties 修改数据库连接 搭建成功 代码审计 sql注入审计 全局搜索${ 查看没有预编译的sql语句&#xff0c;从而找到sql注入功能点 Ctrlalth 查看函…

AIGC大模型ChatGLM2-6B:国产版chatgpt本地部署及体验

1 ChatGLM2-6B介绍 ChatGLM是清华技术成果转化的公司智谱AI研发的支持中英双语的对话机器人。ChatGLM基于GLM130B千亿基础模型训练&#xff0c;它具备多领域知识、代码能力、常识推理及运用能力&#xff1b;支持与用户通过自然语言对话进行交互&#xff0c;处理多种自然语言任务…

干翻Dubbo系列第八篇:Dubbo直连开发核心三要素概述

文章目录 文章说明 一&#xff1a;Dubbo直连开发概念 1&#xff1a;直连设计中的核心组件 (一)&#xff1a;Provider服务的提供者 (二): Consumer服务的访问者 (三)&#xff1a;网络通信明白图 文章说明 本文内容整理自《孙哥说Dubbo系列视频课程》&#xff0c;孙帅老师…

❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件

❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件 cmd或者终端用nvm 安装提示&#xff1a; npm不是内部或外部命令,也不是可运行的程序或批处理文件 原因&#xff08;一&#xff09; 提示这个问题&#xff0c;有可能是Node没有安装&#xff0c;也有可能是没有配置…

【LeetCode】105. 从前序与中序遍历序列构造二叉树 106. 从中序与后序遍历序列构造二叉树

105. 从前序与中序遍历序列构造二叉树 这道题也是经典的数据结构题了&#xff0c;有时候面试题也会遇到&#xff0c;已知前序与中序的遍历序列&#xff0c;由前序遍历我们可以知道第一个元素就是根节点&#xff0c;而中序遍历的特点就是根节点的左边全部为左子树&#xff0c;右…

C高级-day2

思维导图 #!/bin/bash echo "$(head -n 5 /etc/group | tail -1)" mkdir /home/ubuntu/copy cd /home/ubuntu/copy cp /etc/shadow test chown root test chmod o-r,o-w,o-x test#include <myhead.h> //递归实现&#xff0c;输入一个数&#xff0c;输出这个数的…

OpenShift 4 - 可观测性之用 OpenTelemetry+Tempo 实现 Distributed Tracing

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.13 的环境中验证 文章目录 技术架构部署 Distributed Tracing 运行环境安装 minio 环境安装 Grafana Tempo 环境 部署测试应用并进行观测跟踪测试应用1测试应用2 参考 技术架构 Tempo …