JavaScript 实现继承的详细方法:深入探讨

文章目录

    • 前言
    • 一、原型链 (Prototype Chain)
    • 二、借用构造函数 (Constructor Stealing / Classical Inheritance)
    • 三、组合式继承 (Combination Inheritance)
    • 四、原型式继承 (Prototypal Inheritance)
    • 五、寄生式继承 (Parasitic Inheritance)
    • 六、ES6 类 (Class) 和 extends 关键字
    • 结语


前言

JavaScript中的继承机制基于原型链,这与传统的面向对象语言(如Java或C++)有所不同。为了更详细地理解JavaScript中实现继承的方法,我们将深入探讨每一种方式,并提供更详细的代码示例和解释。


一、原型链 (Prototype Chain)

原理:通过将子类的原型设置为父类的实例来实现继承。这是最基础的继承方式。

优点

  • 简单直接。
  • 子类可以访问父类的所有属性和方法。

缺点

  • 所有子类实例共享同一个原型实例,可能导致意外的行为,特别是当涉及到引用类型属性时。
  • 无法传递参数给父类构造函数。

代码示例

function SuperType() {this.property = true;
}SuperType.prototype.getSuperValue = function() {return this.property;
};function SubType() {}// 继承自 SuperType
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType; // 修正构造函数指向
SubType.prototype.getSubValue = function () {return false;
};const instance = new SubType();
console.log(instance.getSuperValue()); // 输出: true

注意:在修改SubType.prototype后,需要重新指定constructor属性,以确保它正确指向SubType

二、借用构造函数 (Constructor Stealing / Classical Inheritance)

原理:在子类构造函数内部调用父类构造函数,并使用call()或apply()方法将this绑定到新创建的对象上来实现继承。

优点

  • 每个子类实例都有自己的一份父类属性副本,避免了引用类型的共享问题。
  • 可以向父类构造函数传递参数。

缺点

  • 不能继承父类的原型方法,只能继承构造函数中的属性和方法。
  • 如果父类构造函数中有复杂的初始化逻辑,可能会重复执行。

代码示例

function SuperType(name) {this.name = name;this.colors = ["red", "blue", "green"];
}function SubType(name, age) {SuperType.call(this, name); // 继承 SuperType 的属性this.age = age;
}const instance1 = new SubType("Nicholas", 30);
instance1.colors.push("black");
console.log(instance1.colors); // ["red", "blue", "green", "black"]const instance2 = new SubType("Greg", 25);
console.log(instance2.colors); // ["red", "blue", "green"]

三、组合式继承 (Combination Inheritance)

原理:结合原型链和借用构造函数的优点,先通过原型链继承原型上的方法,再通过借用构造函数继承实例属性。

优点

  • 解决了上述两种方法的问题,是最常用的继承模式之一。
  • 可以有效避免多次调用父类构造函数的问题。

缺点

  • 构造函数会被调用两次,一次是在设置原型时,另一次是在创建子类实例时。

改进:可以通过Object.create()来避免构造函数被调用两次的问题。

代码示例

function SuperType(name) {this.name = name;this.colors = ["red", "blue", "green"];
}SuperType.prototype.sayName = function() {console.log(this.name);
};function SubType(name, age) {SuperType.call(this, name); // 第二次调用 SuperType()this.age = age;
}// 使用 Object.create 避免构造函数被调用两次
SubType.prototype = Object.create(SuperType.prototype);
SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function() {console.log(this.age);
};const instance = new SubType("Nicholas", 29);
instance.sayName(); // Nicholas
instance.sayAge();  // 29

四、原型式继承 (Prototypal Inheritance)

原理:利用Object.create()方法可以更简洁地创建新对象并指定其原型。

优点

  • 不需要定义构造函数即可实现继承。
  • 简化了对象的创建过程。

缺点

  • 与原型链相似,所有实例共享相同的属性。

代码示例

const person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]
};const anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");const yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barry");console.log(person.friends); // ["Shelby", "Court", "Van", "Rob", "Barry"]

五、寄生式继承 (Parasitic Inheritance)

原理:基于原型式继承,但会增强对象,即在创建的新对象上添加一些新的属性或方法,然后返回这个新对象。

优点

  • 可以在不修改原始对象的情况下扩展功能。

缺点

  • 效率较低,因为每次都要创建一个新的对象。

代码示例

function createAnother(original) {const clone = Object.create(original); // 通过调用函数创建一个新对象clone.sayHi = function() { // 以某种方式增强这个对象console.log("Hi");};return clone; // 返回这个对象
}const person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]
};const anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "Hi"

六、ES6 类 (Class) 和 extends 关键字

原理:ES6引入了类的概念,使得代码看起来更加面向对象,并且简化了继承的实现。

优点

  • 语法清晰,易于理解和使用。
  • 支持静态方法和getter/setter等特性。
  • 提供了更简洁的构造函数调用方式(super())。

代码示例

class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}
}class Dog extends Animal {constructor(name) {super(name); // 调用父类的构造函数}speak() {console.log(`${this.name} barks.`);}
}const d = new Dog('Mitz');
d.speak(); // Mitz barks.

额外特性

  • 静态方法:可以直接在类上定义静态方法,不需要实例化类就可以调用。
  • getter/setter:可以为属性定义存取器方法,以便控制属性的读写行为。
  • 私有字段:通过#前缀可以定义私有字段,限制外部访问。
class MyClass {#privateField = 'private';getPrivateField() {return this.#privateField;}static myStaticMethod() {console.log('Called static method');}
}MyClass.myStaticMethod(); // Called static method
const instance = new MyClass();
console.log(instance.getPrivateField()); // private

结语

JavaScript提供了多种实现继承的方式,每种方式都有其独特的优势和局限性。随着ECMAScript标准的发展,推荐尽可能采用ES6的class和extends关键字来实现继承,因为它们不仅使代码更易读,而且也更容易维护。希望这篇文章能帮助你更好地理解JavaScript中不同的继承方式及其优缺点,并为你选择合适的实现方案提供指导。

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

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

相关文章

海豚调度DolphinScheduler-3.1.9配置windows本地开发环境

源代码下载地址https://dolphinscheduler.apache.org/zh-cn/docs/3.1.9 1.Zookeeper安装与使用 如图下载解压zookeeper安装包,并创建data和log目录 下载地址 https://archive.apache.org/dist/zookeeper/zookeeper-3.6.4/apache-zookeeper-3.6.4-bin.tar.gz 进入…

P1图文解析:初识算法和数据结构

文章目录 前言1、算法例子1.1、查字典(二分查找算法)1.2、整理扑克(插入排序算法)1.3、货币找零(贪心算法) 2、算法与数据结构2.1、算法定义2.2、数据结构定义2.3、数据结构与算法的关系2.4、独立于编程语言…

校园跑腿小程序---轮播图,导航栏开发

hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的在校大学生…

UE材质节点Fresnel

Fresnel节点 ExponentIn 控制边缘透明度 BaseReflectFractionIn 控制中心透明度

浅谈云计算07 | 云安全机制

浅谈云计算安全机制:全方位守护云端世界 一、引言二、加密技术:数据的隐形护盾三、散列机制:数据完整性的忠诚卫士四、数字签名:数据来源与真伪的鉴定专家五、公钥基础设施(PKI):信任的基石六、…

Notepad++上NppFTP插件的安装和使用教程

一、NppFTP插件下载 图示是已经安装好了插件。 在搜索框里面搜NppFTP,一般情况下,自带的下载地址容易下载失败。这里准备了一个下载连接:Release v0.29.10 ashkulz/NppFTP GitHub 这里我下载的是x86版本 下载好后在nodepad的插件里面选择打…

高级运维:源码编译安装httpd 2.4,提供系统服务管理脚本并测试

1.下载httpd 2.4 源码 wget https://archive.apache.org/dist/httpd/httpd-2.4.54.tar.gz 2.解压下载压缩包 tar -zxvf httpd-2.4.54.tar.gz cd httpd-2.4.54 3.安装httpd需要的依赖包 sudo yum groupinstall "Development Tools" -y sudo yum install gcc glibc ap…

8.Bridge 桥接模式(结构型模式)

【1】抽象A>实现细节b 【2】抽象A>抽象B<实现细节b 【3】【抽象B】相对稳定&#xff0c;也可能变化 【实现细节b】频繁变化 【4】抽象B 不稳定&#xff1f; 思考问题&#xff1a;一个变化是平台&#xff08;抽象B&#xff09;的变化&#xff0c;另一个变化是型号…

【PyQt】如何在mainwindow中添加菜单栏

[toc]如何在mainwindow中添加菜单栏 如何在mainwindow中添加菜单栏 主要有两种方法&#xff1a; 1.直接创建mainwindow进行添加 2.使用ui文件加载添加 第二种方法更为常见&#xff0c;可以应用到实际 1.直接创建mainwindow进行添加 import sysfrom PyQt5.QtWidgets import …

基于springboot+vue+微信小程序的宠物领养系统

基于springbootvue微信小程序的宠物领养系统 一、介绍 本项目利用SpringBoot、Vue和微信小程序技术&#xff0c;构建了一个宠物领养系统。 本系统的设计分为两个层面&#xff0c;分别为管理层面与用户层面&#xff0c;也就是管理者与用户&#xff0c;管理权限与用户权限是不…

【Rust】错误处理机制

目录 思维导图 引言 一、错误处理的重要性 1.1 软件中的错误普遍存在 1.2 编译时错误处理要求 二、错误的分类 2.1 可恢复错误&#xff08;Recoverable Errors&#xff09; 2.2 不可恢复错误&#xff08;Unrecoverable Errors&#xff09; 三、Rust 的错误处理机制 3…

Spring Boot教程之五十五:Spring Boot Kafka 消费者示例

Spring Boot Kafka 消费者示例 Spring Boot 是 Java 编程语言中最流行和使用最多的框架之一。它是一个基于微服务的框架&#xff0c;使用 Spring Boot 制作生产就绪的应用程序只需很少的时间。Spring Boot 可以轻松创建独立的、生产级的基于 Spring 的应用程序&#xff0c;您可…

金融项目实战 04|JMeter实现自动化脚本接口测试及持续集成

目录 一、⾃动化测试理论 二、自动化脚本 1、添加断言 1️⃣注册、登录 2️⃣认证、充值、开户、投资 2、可重复执行&#xff1a;清除测试数据脚本按指定顺序执行 1️⃣如何可以做到可重复执⾏&#xff1f; 2️⃣清除测试数据&#xff1a;连接数据库setup线程组 ①明确…

【Uniapp-Vue3】@import导入css样式及scss变量用法与static目录

一、import导入css样式 在项目文件中创建一个common文件夹&#xff0c;下面创建一个css文件夹&#xff0c;里面放上style.css文件&#xff0c;编写的是公共样式&#xff0c;我们现在要在App.vue中引入该样式。 在App.vue中引入该样式&#xff0c;这样就会使样式全局生效&#…

大疆机场及无人机上云

最近基于大疆上云api进行二次开发&#xff0c;后面将按照开发步骤对其进行说明&#xff01;

Golang笔记——hashmap

本文详细介绍golang的哈希表的底层实现、扩容机制、插入查询过程以及并发安全性。 文章目录 定义Key无序性Key唯一性Key可比性 基本使用底层实现哈希表实现hmapbucket 数据结构bmap链地址法哈希冲突负载因子 扩容增量扩容等量扩容 查找过程插入过程删除流程非并发安全map 的线程…

【网络】:网络编程套接字

目录 源IP地址和目的IP地址 源MAC地址和目的MAC地址 源端口号和目的端口号 端口号 VS 进程ID TCP协议和UDP协议 网络字节序 字符串IP和整数IP相互转换 查看当前网络的状态 socket编程接口 socket常见API 创建套接字&#xff08;socket&#xff09; 绑定端口号&…

el-descriptions-item使用span占行不生效

需要实现的效果是客户状态单独占满一行 错误代码&#xff1a; <el-descriptions title"基本信息" :column"3"> <el-descriptions-item label"公司电话:">Suzhou</el-descriptions-item><el-descriptions-item label"…

vue城市道路交通流量预测可视化系统

文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站、收藏、不迷路&#xff01; 项目亮点 编号&#xff1a;R09 &#x1f687; 网站大屏管理三大前端、vuespringbootmysql、前后端分离架构 &#x1f687; 流量预测道路查询…

Elasticsearch:使用 Playground 与你的 PDF 聊天

LLMs作者&#xff1a;来自 Elastic Toms Mura 了解如何将 PDF 文件上传到 Kibana 并使用 Elastic Playground 与它们交互。本博客展示了在 Playground 中与 PDF 聊天的实用示例。 Elasticsearch 8.16 具有一项新功能&#xff0c;可让你将 PDF 文件直接上传到 Kibana 并使用 Pla…