【ES6】Class继承-super关键字

news2024/11/15 14:01:37

目录

  • 一、前言
  • 二、ES6与ES5继承机制区别
  • 三、super作为函数
    • 1、构造函数this
      • 1)、首先要明确this指向
        • ①、普通函数
        • ②、箭头函数
        • ③、注意事项
      • 2)、其次要明确new操作符做了哪些事情
    • 2、super()的用法及注意点
      • 1)、用法
      • 2)、注意点
  • 四、super作为对象
    • 1、super在子类普通方法中作为对象
    • 2、super在子类静态方法中作为对象
  • 五、总结

一、前言

类的继承可以通过extends实现,让子类继承父类的属性和方法,而在子类内部(构造函数constructor)必须调用super()实现继承(super()代表父类构造函数,调用之后生成一个继承父类的this对象)

二、ES6与ES5继承机制区别

  1. ES5的继承机制,是先创造一个独立的子类的实例对象,然后再将父类的方法添加到这个对象上面,即“实例在前,继承在后”
  2. ES6的继承机制,则是先将父类的属性和方法,加到一个空对象上面,然后再将该对象作为子类的实例,即“继承在先,实例在后”
  3. 所以ES6的继承必须先调用super(),这样会生成一个继承父类的this对象,没有这一步就无法继承父类。这意味着新建子类实例时,父类的构造函数必定会先运行一次
  4. super既可以作为函数使用,也可以作为对象使用,两种方法有很大的不同

三、super作为函数

super()作为函数调用时(子内constructor内部必须调用),代表父类的构造函数,调用之后生成的是子类的实例,即super()内部this指向的是子类的实例(由构造函数this指向可得出,构造函数内部this指向的是生成的实例,而super()调用之后生成的是子类实例)

1、构造函数this

1)、首先要明确this指向

①、普通函数

内部的this指向函数运行时所在的对象,也即指向函数的直接调用者

  • 如果一个函数在全局环境运行,this就指向顶层对象(浏览器中为window对象)
  • 如果一个函数作为某个对象的方法运行,this就指向那个对象
  • 如果一个函数作为构造函数,this指向它的实例对象
②、箭头函数

没有自己的this对象,内部的this就是定义时上层作用域中的this

③、注意事项

构造函数只能是普通函数,箭头函数不能作为构造函数(没有prototype),不可以对箭头函数使用new命令,否则会抛出一个错误

2)、其次要明确new操作符做了哪些事情

  1. 创建一个空对象instance({})
  2. 将instance的[[prototype]]属性指向构造函数fn的原型(即instance.[[prototype]] = fn.prototype),也即instance_ proto_要指向构造函数的原型prototype
  3. 执行构造函数,使用 call/apply 改变 this 的指向 => 让this指向创建出来的实例instance
  4. 若函数返回值的是对象类型则作为new方法的返回值返回,否则返回刚才创建的全新对象
function isObject (target) {
  return (typeof target === 'object' || typeof target === 'function') && target !== null
}
function myNew (fn, ...args) {
  // 使用Object.create(Y),就说明实例的__proto__指向了Y,即实例的原型对象是Y
  // Object.create():创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
  // 并且执行[[Prototype]]链接; 通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。
  const instance = Object.create(fn.prototype)
  // 改变this指向
  let res = fn.apply(instance, args)

  // 若res是个对象就直接返回,否则返回创建的对象
  // return res instanceof Object ? res : instance
  return this.isObject(res) ? res : instance
}

如果不用new操作符直接调用,那么构造函数就相当于普通函数了,执行对象就 是window(严格模式下为undefined),即this指向了window(严格模式下为undefined)

由此可以看出,构造函数内部this指向实例对象

2、super()的用法及注意点

1)、用法

下面代码中,B是A的子类,B继承A,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例
B里边的super()在这里相当于A.prototype.constructor.call(this)

class A {
  constructor() {
    // `new.target`指向当前正在执行的函数
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
// 在`super()`执行时,它指向的是子类`B`的构造函数,而不是父类`A`的构造函数
// 即`super()`内部的`this`指向的是`B`的实例
new B() // B

new.target指向被new调用的构造函数:在普通的函数调用中(函数调用),new.target的值undefined;在类的构造方法中,new.target指向直接被new执行的构造函数

2)、注意点

  • 子类的构造函数constructor内必须执行一次super函数
    • 这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象
    • 新建子类实例时,父类的构造函数必定会先运行一次
  • 在子类的构造函数中,只有调用super()之后,才可以使用this关键字,否则会报错
    • super()调用之后才能让子类实例继承父类,然后才能在该基础上对子类实例进行属于它的特有操作
  • 不管有没有显示定义,任何一个子类都有constructor()方法,而且里边默认会调用super()
  • 作为构造函数时,super()只能用于在子类的构造函数之中,用在其他地方就会报错
    在这里插入图片描述
  • 使用super()的时候,必须显示指定是作为函数,还是作为对象使用,否则会报错
    在这里插入图片描述
  • 由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字
    在这里插入图片描述

四、super作为对象

super作为对象时,在子类普通方法中,指向父类的原型对象;在子类静态方法中,指向父类

1、super在子类普通方法中作为对象

当super作为对象在子类普通方法(非静态方法)中使用时,由于super指向父类的原型对象,所以定义在父类原型对象上的属性和方法都可以被访问到,而定义在父类实例上(父类构造函数this.xxx)的方法或属性无法被访问到

在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例,相当于执行的是super.fn.call(this)

下面的代码中,父类A有定义在原型对象的属性b和方法p(),还有定义在实例对象的属性a;可以看到的是,定义在原型对象的b和p都能被super.访问到,而定义在实例对象的a无法被访问到

class A {
  constructor() {
    this.a = 123 // 定义在A的实例上的属性a
  }
  // 定义在A.prototype上的方法
  p() {
    return 2;
  }
}

A.prototype.b = 123 // 定义在A原型对象的属性b

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2 => 相当于console.log(A.prototype.p())
  }
  
  getData() {
    console.log(super.a) // a定义在父类的实例对象上,所以为undefined
    console.log(super.b) // b定义在父类的原型对象上,所以为123
  }
}

let b = new B();

b.getData()

在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例,相对于执行的是super.fn.call(this)

class A {
  constructor() {
    this.x = 1; // 父类实例的x是1
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2; // 子类实例的x是2
  }
  m() {
    // 实际上执行的是`super.print.call(this)`
    super.print(); // 调用父类的方法,但是方法内部this是子类实例,所以输出2
  }
}

let b = new B();
b.m() // 2

由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性

class A {
  constructor() {
    this.x = 1;
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}

let b = new B();

上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined

2、super在子类静态方法中作为对象

当super作为对象在子类静态方法中使用时,这时super将指向父类,而不是父类的原型对象。在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例

class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }

  myMethod(msg) {
    console.log('instance', msg);
  }
}

class Child extends Parent {
  static myMethod(msg) {
    // super作为对象在子类静态方法中使用,super指向的是父类
    // 也即要调用父类的myMethod方法,那就是父类的静态方法Parent.myMethod
    // 而属于一个类的方法,就是静态方法,也就是由static关键字定义的方法
    super.myMethod(msg); // 调用的是Parent.myMethod()
  }

  myMethod(msg) {
    // super作为对象在子类普通方法中使用,super指向的是父类的原型对象
    // 此时的super.myMethod()也即Parent.prototype.myMethod()
    // 由class语法糖和原来构造函数创建类的写法相对比可知
    super.myMethod(msg);
  }
}

Child.myMethod(1); // static 1

var child = new Child();
child.myMethod(2); // instance 2

五、总结

通过本文的介绍,我们了解了在ES6中使用extends关键字实现继承,并且学习了super()在子类中的用法和作用以及注意点。super()的正确使用可以让我们更加方便地在子类中调用父类的构造函数和方法,从而更加灵活地使用面向对象编程。希望本文能够帮助各个小伙伴更好地理解ES6中Class继承的super()关键字。

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

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

相关文章

算法的四大思想之一:动态规划

一、动态规划 什么是动态规划? 动态规划(Dynamic Programming,简称DP)是一种解决问题的算法思想,它将一个大问题拆分成多个相互重叠的子问题,并且通过解决这些子问题来求解原始问题 核心思想 拆分大问题…

vue3+luckyexcel+php在线编辑excel文件

开发过程中,需要开发一个在线编辑excel文档的功能,找到了这个合适的组件 Luckysheet ,一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。 可以导入文档,预览、编辑、保存、导出等功能,可以满…

RabbitMQ 和 Kafka 对比

本文对RabbitMQ 和 Kafka 进行下比较 文章目录 前言RabbitMQ架构队列消费队列生产 Kafka本文小结 前言 开源社区有好多优秀的队列中间件,比如RabbitMQ和Kafka,每个队列都貌似有其特性,在进行工程选择时,往往眼花缭乱,不…

【MATLAB】PSO粒子群优化BiLSTM(PSO_BiLSTM)的时间序列预测

有意向获取代码,请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 基于PSO粒子群优化的BiLSTM的时间序列预测算法的基本原理如下: 「双向长短时记忆(BiLSTM)模型」:这是一种深度学习模型,特别适用…

从现场到远程:PLC网关设备售后服务升级换代

问题 作为自动化企业,以前在调试PLC程序时,不得不在现场调试,遇到软件维护和售后服务时,甚至给公司带来一定的经营成本和维护成本的压力,PLC网关正好解决了这一难题。 PLC工业网关是可以让工业PLC设备轻松接入互联网…

GPT分区格式

GPT分区格式 [rootlocalhost ~]# gdisk /dev/sdb -bash: gdisk: 未找到命令 [rootlocalhost ~]# yum -y install gdisk- gdisk命令用于查看磁盘使用情况和磁盘分区(GPT分区格式) - 命令格式:gdisk [选项...] [设备路径] - 常用选项&…

linux 网络工具(二)

linux 网络工具 1. ip命令簇4.1 address4.2 link4.3 route4.4 rule 2. 其他常用命令2.1 ifup/ifdown2.2 配置主机名2.3 设置DNS服务器指向2.4 配置域名解析2.5 ss2.6 路由相关配置文件2.7 查看机器可用端口2.8 traceroute2.9 dhclient 1. ip命令簇 Linux的ip命令和ifconfig类似…

案例189:基于微信小程序的高校教务管理系统设计与实现

文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder …

Three.js基础入门介绍——Three.js学习三【借助控制器操作相机】

在Three.js基础入门介绍——Three.js学习二【极简入门】中介绍了如何搭建Three.js开发环境并实现一个包含旋转立方体的场景示例,以此为前提,本篇将引进一个控制器的概念并使用”轨道控制器”(OrbitControls)来达到从不同方向展示场…

使用 SSH 方式实现 Git 远程连接GitHub

git是目前世界上最先进的分布式版本控制系统,相比于SVN,分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作!当有网络的时候,再把本地提交推送一下就完成了同步&…

【信息安全原理】——入侵检测与网络欺骗(学习笔记)

📖 前言:在网络安全防护领域,防火墙是保护网络安全的一种最常用的设备。网络管理员希望通过在网络边界合理使用防火墙,屏蔽源于外网的各类网络攻击。但是,防火墙由于自身的种种限制,并不能阻止所有攻击行为…

open_vins 安装(ubuntu18.04 opencv3.2.0)

openvins官网 Getting Started Installation Guide (ROS1 and ROS2) | OpenVINS Ubuntu 18.04 ROS 1 Melodic (uses OpenCV 3.2) 这里他指的是ros1 melodic,他们用的opencv3.2测试过。 open_vins 官方给的组合Ubuntu 18.04 ROS 1 Melodic (uses OpenCV 3.2) Ub…

k8s二进制部署2

部署 Worker Node 组件 //在所有 node 节点上操作 #创建kubernetes工作目录 mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs} #上传 node.zip 到 /opt 目录中,解压 node.zip 压缩包,获得kubelet.sh、proxy.sh cd /opt/ unzip node.zip chmod x kubelet.…

视觉学习笔记13——既是模型,又是模型中转站的onnx

系列文章目录 入门级深度学习环境搭建 文章目录 系列文章目录前言一、ONNX是什么?二、环境安装1、在 Anaconda 环境中安装 onnx2、在 Anaconda 环境中卸载 onnx3、anaconda 安装onnxruntime 未完待续。。。 前言 假设一个场景:现在某组织因为主要开发用…

Ubuntu中fdisk磁盘分区并挂载、扩容逻辑卷

Ubuntu中fdisk磁盘分区并挂载、扩容逻辑卷 一:fdisk磁盘分区并挂载1.查看磁盘分区信息2.分区3.强制系统重新读取分区(避免重启系统)4.格式化分区5.创建挂载目录6.设置开机自动挂载:7.验证并自动挂载(执行了该命令不需要重启系统)8.查看挂载007.异常情况处…

Feature Prediction Diffusion Model for Video Anomaly Detection 论文阅读

Feature Prediction Diffusion Model for Video Anomaly Detection论文阅读 Abstract1. Introduction2. Related work3. Method3.1. Problem Formulation3.2. Feature prediction diffusion module 3.3. Feature refinement diffusion module4. Experiments and discussions4.1…

Flink1.17实战教程(第六篇:容错机制)

系列文章目录 Flink1.17实战教程(第一篇:概念、部署、架构) Flink1.17实战教程(第二篇:DataStream API) Flink1.17实战教程(第三篇:时间和窗口) Flink1.17实战教程&…

如何在无公网IP环境使用Windows远程桌面Ubuntu

文章目录 一、 同个局域网内远程桌面Ubuntu二、使用Windows远程桌面连接三、公网环境系统远程桌面Ubuntu1. 注册cpolar账号并安装2. 创建隧道,映射3389端口3. Windows远程桌面Ubuntu 四、 配置固定公网地址远程Ubuntu1. 保留固定TCP地址2. 配置固定的TCP地址3. 使用…

使用Visual Studio 2022 winform项目打包成安装程序.exe

winform项目打包 1.安装扩展插件 Microsoft Visual Studio Installer Projects 20222.在解决方案上新建一个setup project 项目3.新建成功如下图,之后添加你的winform程序生成之后的debug下的文件4.在Application Folder上点击右键->Add->项目输出->主输出…

从实际业务问题出发去分析Eureka-Server端源码

文章目录 前言1.EnableEurekaServer2.初始化缓存3.jersey应用程序构建3.1注册jeseryFilter3.2构建JerseyApplication 4.处理注册请求5.registry() 前言 前段时间遇到了一个业务问题就是k8s滚动发布Eureka微服务的过程中接口会有很多告警,当时…