【JavaScript】面向对象继承

news2025/2/28 21:27:51

目录

  • 一、前言
  • 二、问题是最好的老师
    • 1、为什么要使用继承
    • 2、如何实现继承
  • 三、面向对象继承方式
    • 1、借用构造函数继承
      • 1)、优点
      • 2)、缺点
    • 2、原型链继承
      • 1)、完整代码
      • 2)、缺点
    • 3、组合继承(原型链+借用构造函数)
      • 1)、优点
      • 2)、缺点
    • 4、原型式继承
      • 1)、实现函数的三种方式
        • ①、借用构造函数原型的指向(最早使用的方法)
        • ②、利用Object.setPrototypeOf可以改变原型
        • ③、利用Object.create(),本质上内部代码实现跟方式一的实现是一样的 (最新版本ECMAScript提供)
      • 2)、对象与对象之间的继承
      • 3)、类与类之间的继承
        • ①、替换到原型链继承的一行代码
        • ②完整代码
      • 4)、优点
      • 5)、缺点
    • 5、ES6-class继承
      • 1)、class继承
      • 2)、优点
      • 3)、缺点
  • 四、总结

一、前言

  对象编程很重要的一方面,就是对象的继承。A对象通过继承B对象,就能直接拥有B对象的属性和方法。这对于代码的复用是非常有用的。
  大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript语言的继承不通过class(ES6引入了class语法),而是通过“原型对象”(prototype)实现。

二、问题是最好的老师

在正题开始之前,先问自己两个问题:
1. 继承解决了什么问题?为什么要使用继承?
2. 如何实现继承?

1、为什么要使用继承

通过以下图片我们可以看出不同的类,存在着大量的重复代码,这样导致难以维护。所以需要进行封装,需要继承
在这里插入图片描述

2、如何实现继承

function Person(name,age){
  this.name=name
  this.age=age
}
Person.prototype.say=function(){
  console.log(this.name,"hello")
}

//继承
function Student(name,age,grade){
  // Person.call(this,name,age)
  Person.apply(this,[name,age])
  this.grade=grade
}

//原型继承
Student.prototype = new Person()
//在继承的基础上增加方法
Student.prototype.printGrade = function(){
  console.log(this.grade,100)
}

上面代码,存在两个类(Person,Student),那么二者在内存中的表现如何:

在这里插入图片描述

类与类之间相互独立,毫无相关。那么应该怎么产生关联呢?

在这里插入图片描述

上图表示,需要创建一个中间对象,那么改中间对象的作用:

  1. 子类的函数原型对象指向中间对象
  2. 中间对象的原型指向父类的函数原型对象

最后的结果就是子类和父类间接挂钩,继承也就在此诞生。好啦,问了自己两个问题并且搞清楚之后,正题就可以开始啦。

三、面向对象继承方式

1、借用构造函数继承

子类直接调用父类函数

function Person(name,age){
  this.name=name
  this.age=age
}
Person.prototype.say=function(){
  console.log(this.name,"hello")
}

//继承
function Student(name,age,grade){
  //call、apply都可以借用父类的代码
  // Person.call(this,name,age)
  Person.apply(this,[name,age])
  this.grade=grade
}

1)、优点

解决了传递参数的问题

2)、缺点

仅仅继承了父类的属性,并没有继承原型上的方法

2、原型链继承

将子类的函数原型(prototype)指向父类的函数原型(prototype),这就是原型链继承

Student.prototype = Person.prototype 

是以上这种形式的吗?当然不是,这样写的话,针对一个子类也还行,但是存在多个子类。都是使用了同一个原型(即父类的原型),那么子类与子类之间就会相互影响。那么该如何写呢?

//原型继承
Student.prototype = new Person()
//在继承的基础上增加方法
Student.prototype.printGrade = function(){
  console.log(this.grade,100)
}

//拆分来写
var obj = new Person()
Student.prototype = obj

创建一个实例对象obj,obj就是所谓的中间对象,那么子类的原型指向它,子类与子类之间就不会相互影响。

1)、完整代码

function Person(name,age){
  this.name = name
  this.age = age
}
Person.prototype.say=function(){
  console.log(this.name,"hello")
}

//继承
function Student(name,age,grade){
  // Person.call(this,name,age)
  Person.apply(this,[name,age])
  this.grade = grade
}

//原型继承
Student.prototype = new Person()
//在继承的基础上增加方法
Student.prototype.printGrade = function(){
  console.log(this.grade,100)
}
var obj = new Student("xiaoming",18,100)
console.log(obj)

obj.say()
obj.printGrade()

2)、缺点

  1. 参数不好传递(子类的name不好传递到父类中)
  2. 子类的实例对象类型错误,继承的name属性也没有显示

类型错误的原因:Student.prototype.constructor.name,由于本身丢失,中间对象又没有constructor属性,就会寻找到Person的函数原型上,所以类型为Person

3、组合继承(原型链+借用构造函数)

&emps;&emps;为了解决原型链继承中存在的问题,开发人员提供了一种新的技术:constructor stealing(有很多名称:借用构造函数或者称之为经典继承或者称之为伪造对象)

借用继承的做法非常简单:在子类型构造函数的内部调用父类型构造函数

  • 因为函数可以在任意的时刻被调用,因此通过call()和apply()方法也可以在新创建的对象上执行构造函数
function Person(name,age){
  this.name=name
  this.age=age
}
Person.prototype.say=function(){
  console.log(this.name,"hello")
}

//继承
function Student(name,age,grade){
  // Person.call(this,name,age)
  Person.apply(this,[name,age])
  this.grade=grade
}

//原型继承
Student.prototype = new Person()
//在继承的基础上增加方法
Student.prototype.printGrade = function(){
  console.log(this.grade,100)
}

var obj = new Student("xiaoming",99,11)
console.log(obj)

obj.say()
obj.printGrade()

1)、优点

  1. 解决了传递参数的问题
  2. 解决了子类的类型不对的问题(指定子类的constructor)

2)、缺点

  1. 构造函数会被调用两次:一次在创建子类型原型对象的时候,一次在创建子类型实例的时候
  2. 父类型的属性(示例:name属性)会有两份:一份在中间对象中,一份在子类型示例中

4、原型式继承

&emps;&emps;这种模式要从道格拉斯·克罗克福德(Douglas Crockford,著名的前端大师,JSON的创立者)。在2006年写的一篇文章说起:Prototypal Inheritance in JavaScript(在JS中使用原型式继承)

怎么实现继承?需要中间对象,最开始我们就已经提及了。

那么是不是可以自己定义一个对象,使其_proto_指向另外一个对象呢?

可以定义一个函数,该函数接受一个对象作为参数,函数体内部创建一个新的对象,使其新对象的原型指向参数对象,然后返回新的对象。实现类似功能的函数,被称为原型式继承函数

1)、实现函数的三种方式

①、借用构造函数原型的指向(最早使用的方法)
 function createObject1(obj) {
     function Fn() {}
     Fn.prototype = obj  
     return new Fn()
 }
②、利用Object.setPrototypeOf可以改变原型
 function createObject2(obj) {
   var newObj = {};
   Object.setPrototypeOf(newObj, obj);  // newObj的原型指向obj
   return newObj;
 }
③、利用Object.create(),本质上内部代码实现跟方式一的实现是一样的 (最新版本ECMAScript提供)
const newObj = Object.create(obj)

既然有了原型式继承函数,那么就可以实现对象与对象之间的继承,类与类之间的继承了

2)、对象与对象之间的继承

 const Person = {
   name: "父类",
   play: function () {
     console.log("play~~");
   },
 };
 const obj = Object.create(Person);
 console.log(obj); // {}
 console.log(obj.__proto__); // { name: '父类', play: [Function: play] }

3)、类与类之间的继承

①、替换到原型链继承的一行代码
 Student.prototype = new Person()// 修改成:
 Student.prototype = Object.create(Person.prototype)
②完整代码
function Person(name,age){
  this.name=name
  this.age=age
}
Person.prototype.say=function(){
  console.log(this.name,"hello")
}

//继承
function Student(name,age,grade){
  // Person.call(this,name,age)
  Person.apply(this,[name,age])
  this.grade=grade
}

//原型继承
Student.prototype = Object.create(Person.prototype)
//在继承的基础上增加方法
Student.prototype.printGrade = function(){
  console.log(this.grade,100)
}

var obj = new Student("小明",18,100)
console.log(obj)

obj.say()
obj.printGrade()

4)、优点

创建中间对象简单,容易实现继承

5)、缺点

必须与其他方式的继承才能实现完成的继承

5、ES6-class继承

1)、class继承

ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比ES5的通过修改原型链实现继承要清晰和方便很多。

ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Person.apply(this)。ES6的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法,然后再用子类的构造函数修改this))
需要注意:class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的

//父类
class Person{
  //构造函数
  constructor(name,age){
    this.name = name
    this.age = age
  }

  say(){
    console.log(this.name,"hello")
  }
}

//子类
//extends 原型继承
class Student extends Person{
  constructor(name,age,grade){
    //super 超类
    super(name,age) //Person.call(this,name,age)
    this.grade = grade
  }
  say(){
    //如果想要先打印父类中的say方法,就需要使用super去调用父类中的say方法即可
    super.say()
    console.log(this.name,"您好")
  }
}
var obj = new Student("小明",100,100)
console.log(obj)
obj.say()

2)、优点

代码量少,思路清晰,便于理解

3)、缺点

浏览器兼容性的问题。ie10及以下都不支持。

ES6中使用到了super关键字,欢迎大家对[ES6]Class继承-super关键字这篇博客提出需要改善的建议

四、总结

在JavaScript中,面向对象继承有多种实现方式,每种方式都有其优缺点。选择合适的继承方式取决于具体的需求和场景。通过本文的介绍,希望各位读者能够更好的理解JavaScript中的面向对象继承,并能够灵活运用于实际开发中。有任何问题,可以通过评论进行交流~!

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

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

相关文章

Rust学习笔记000 安装

安装命令 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh $ curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh info: downloading installerWelcome to Rust!This will download and install the official compiler for the Rust programming la…

C语言转WebAssembly的全流程,及Web端调用测试

第一步:安装环境 参考网址:https://emscripten.org/docs/getting_started/downloads.html 具体过程: 克隆代码:git clone https://github.com/emscripten-core/emsdk.git进入代码目录:cd emsdk获取最新远端代码&…

关键字:try-catch关键字

在 Java 中,try-catch关键字用于异常处理。它们允许编写代码来捕获和处理异常,以确保程序能够在出现问题时合理地处理它们而不会崩溃。 以下是try-catch关键字的基本语法: 在try块中编写可能会抛出异常的代码。如果在try块中的任何代码抛出…

【Proteus仿真】【STM32单片机】自动除湿器系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器,使用按键、LCD1602液晶、DHT11温湿度、继电器除湿模块等。 主要功能: 系统运行后,LCD1602显示DHT11传感器检测的湿度值阈值…

将数据“0x0f“写入AT24C02再读出送P1口显示

#include <reg51.h> // 包含51单片机寄存器定义的头文件 #include <intrins.h> //包含_nop_()函数定义的头文件 #define OP_READ 0xa1 // 器件地址以及读取操作,0xa1即为1010 0001B #define OP_WRITE 0xa0 // 器件地址以及写入…

浅析xxl-obj分布式任务调度平台RCE漏洞

文章目录 前言本地环境搭建1、初始化数据库2、搭建调度中心3、搭建出执行器 XXL-JOB漏洞1、后台弱口令->RCE2、未授权API->RCE3、默认accessToken4、CVE-2022-361575、SSRF漏洞->RCE 总结 前言 在日常开发中&#xff0c;经常会用定时任务执行某些不紧急又非常重要的事…

jmeter的常用功能及在测试中的基本使用和压测实战

Jmeter基础功能 了解Jmeter的常用组件 元件&#xff1a;多个类似功能组件的容器&#xff08;类似于类&#xff09; 一&#xff1a;Test Plan&#xff08;测试计划&#xff09; 测试计划通常用来给测试的项目重命名&#xff0c;使用多线程脚本运行时还可以配置线程组运行方式…

【HR非技术问题面试篇】你怎么看待加班?

你对加班怎么看待? &#x1f60a;老油条经典回答系列 &#x1f60a;老油条经典回答系列 这种问题&#xff0c;怎么回答都可以&#xff0c;我觉得重要的实时表达你自己。如果你就不想加班&#xff0c;也没必要勉强说自己爱加班&#xff0c;结果入职之后干的不开心。 不过&…

S32K312使用ITCM向FLASH代码区写入数据

使用C40_IP的系列方法向FLASH代码区写入数据时&#xff0c;程序会卡死在读取写操作的状态C40_Ip_MainInterfaceWriteStatus()这个方法中。本文主要介绍S32K312通过ITCM的方式&#xff0c;通过C40_IP的方法向FLASH代码区成功写入数据的方法和步骤。 首先&#xff0c;验证一下C4…

configparser.NoSectionError: No section: ‘***‘解决方案

大家好,我是水滴~~ 本文主要介绍 configparser.NoSectionError: No section: *** 问题的解决方案,希望能对你有所帮助。 《Python入门核心技术》专栏总目录・点这里 文章目录 1.问题描述2.解决方案3. INI 文件介绍1.问题描述 我们在使用 Python 的配置文件的时候,经常会看…

软件有效找不到dll文件,五种可靠的解决dll方法分享

电脑已经成为我们生活和工作中不可或缺的工具。然而&#xff0c;由于各种原因&#xff0c;电脑可能会出现一些问题&#xff0c;其中之一就是“电脑提示dll文件缺失”。这个问题可能会给我们的生活和工作带来很大的困扰&#xff0c;因此&#xff0c;我希望通过分享我的心得体会&…

005、数据类型

1. 关于数据类型 Rust中&#xff0c;每个值都有其特定的数据类型&#xff0c;Rust会根据数据的类型来决定如何处理它们。 Rust是一门静态类型语言&#xff0c;它在编译程序的过程中就需要知道所有变量的具体类型。在大部分情况下&#xff0c;编译器可以根据我们如何绑定、使用变…

揭秘HTTP与HTTPS:保障安全的网页传输协议之争

目录 1、前言 2、HTTP与HTTPS的概念及区别 2.1 HTTP的定义与特点 2.2 HTTPS的定义与特点 2.3 HTTP与HTTPS的区别 3、HTTP的工作原理及安全隐患 3.1 HTTP的工作流程 3.2 HTTP的安全隐患 4、HTTPS的工作原理及优势 4.1 HTTPS的工作流程 4.2 HTTPS的加密算法 4.3 HTTP…

python+opencv实现图片/短视频一键去水印

目录 0 前言1 准备工作2 读取图片或视频3 添加回调获取鼠标绘制水印区域4 调用opencv函数5 绘制蒙版主循环6 去水印主循环总结 0 前言 在制作ppt个人文章或者分享图片过程中&#xff0c;经常会遇到一些带有水印的情况&#xff0c;不少人都希望能够去除这些水印&#xff0c;提高…

java企业网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web企业网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

蓝桥杯C/C++程序设计——成绩统计

题目描述 小蓝给学生们组织了一场考试&#xff0c;卷面总分为 100 分&#xff0c;每个学生的得分都是一个 0 到 100 的整数。 如果得分至少是 60 分&#xff0c;则称为及格。如果得分至少为 85 分&#xff0c;则称为优秀。 请计算及格率和优秀率&#xff0c;用百分数表示&am…

Zookeeper实现分布式锁和注册中心

目录 分布式锁 实现方式 分布式锁场景如何选择Redis和zookeeper 用InterProcessMutex实现分布式锁 zookeeper实现注册中心 分布式锁 实现方式 数据库唯一索引Redis的setnxZookeeper创建临时节点及监听机制Zookeeper创建临时有序节点 分布式锁场景如何选择Redis和zookeepe…

Apple Unity Plugins 接入GameCenter 崩溃解决方案

目录 问题问题原因解决方案可直接使用的UnityPlugins 问题 调用 GKLocalPlayer.Local.FetchItems() 程序崩溃&#xff0c;报错&#xff1a;Thread 1: EXC_BAD_ACCESS (code257, address0x8000000000000002) 启动崩溃&#xff0c;报错&#xff1a;Library not loaded: rpath/Ap…

Ubuntu 22.04 安装ftp实现与windows文件互传

Ubuntu 22.04 安装ftp实现与windows文件互传 1、配置安装 安装&#xff1a; sudo apt install vsftpd -y使能开机自启&#xff1a; sudo systemctl enable vsftpd 启动&#xff1a; sudo systemctl start vsftpd创建ftp工作目录&#xff1a; sudo mkdir -p /home/ftp/uftp…

FPGA实现 NIC 10G 网卡,纯verilog代码编写,提供工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的UDP方案3、10G网卡基本性能简介4、详细设计方案接口概述PCIe HIPDMA IFAXI总线接口时钟同步处理TXQ和RXQ队列TXCQ和RXCQ队列完成EQ MAC PHY流水线队列管理发送调度程序端口和接口数据路径以及发送和接收引擎分段内存接口 5、vivado工程详…