编码遗传学:JavaScript 继承之道

2023-12-14 11:44

本文主要是介绍编码遗传学:JavaScript 继承之道,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引入

JavaScript中的继承问题是一个很重要的知识点,很多面试都会问到。本文主要来详细地讲解JavaScript实现继承的不同方法。其中包括原型链继承、构造函数基础、组合式继承以及寄生组合式继承等ES5的实现方式,也会介绍ES6新出现的class继承

原型链继承

原型链继承简单地将子类的原型对象指向父类实例,这样子类实例在无法找到对应的属性或方法时会继续向其原型对象,即父类实例上查找,从而实现对父类属性和方法的继承。

function Person() {this.name = 'N-A';
}
Person.prototype.getName = function() {return this.name;
}
function Student() {}
Student.prototype = new Person();
// 原型的实例等于自身
Student.prototype.constructor = Student;
const student = new Student();
console.log(student.name); // N-A
console.log(student.getName()); // N-A

但这种方法存在一些缺陷:

1.引用类型共享:由于所有子类实例的原型都指向同一个父类实例,当子类实例修改继承的引用类型属性时,会影响所有子类实例,因为它们共享相同的原型对象。

function Person() {this.obj = {name: 'N-A',age: 5};
}
function Student() {}
Student.prototype = new Person();
// 原型的实例等于自身
Student.prototype.constructor = Student;
const student1 = new Student();
student1.obj.name = 'CSDN';
const student2 = new Student();
console.log(student2.obj.name); // CSDN

2.无法传参:在创建子类实例时无法向父类构造函数传递参数,因此无法像使用 super() 一样实现在子类中调用父类构造函数的功能,限制了灵活性和定制性。

构造函数继承

构造函数继承通过在子类构造函数中调用父类构造函数并使用子类的 this,将父类的成员属性和方法直接挂载到子类实例上。这种方式避免了子类实例共享一个原型实例,同时也能够向父类构造函数传递参数。

function Person(name) {this.name = name
}
Person.prototype.getName = function() {return this.name;
}
function Student() {Person.apply(this, arguments);
}const student = new Student('N-A');
console.log(student.name); // N-A

然而,这种继承方式同样也存在缺陷:无法继承父类原型上的属性和方法,子类只能继承到父类构造函数中定义的属性和方法,而无法直接访问和继承父类原型上的属性和方法。这导致子类无法复用父类原型链上的方法,降低了代码的复用性和灵活性。

虽然构造函数继承解决了一些原型链继承的问题,但它的局限性在于无法继承父类原型上的属性和方法,限制了子类的功能扩展和代码复用。

组合式继承

组合继承结合了原型链继承和构造函数继承的优点,通过在子类构造函数中调用父类构造函数来实现对父类属性的继承,同时利用原型链继承来继承父类原型上的方法和属性

function Person(name) {this.name = name;
}
Person.prototype.getName = function() {return this.name;
}
function Student() {// 构造函数继承Person.apply(this, arguments)
}
// 原型式继承
Student.prototype = new Person();// 原型的实例等于自身
Student.prototype.constructor = Student;const student = new Student('N-A');
console.log(student.name); // N-A
console.log(student.getName()); // N-A

但是,这种继承方式也存在一些缺陷:重复调用构造函数:每次创建子类实例时,都会执行两次构造函数。一次是通过 Person.apply 将父类的属性赋予子类实例,另一次是使用 new Person() 创建父类实例,并将其作为子类原型的一部分。这不影响对父类属性的继承,但会导致子类原型中存在两份相同的属性和方法,造成资源浪费和内存占用。

尽管组合继承解决了原型链继承和构造函数继承各自的缺陷,但重复调用构造函数会导致资源浪费,使得子类原型上存在冗余的属性和方法,不够优雅。

寄生式组合继承

寄生式组合继承是一种通过在子类构造函数中使用 Object.create 方法来优化组合继承的方式,解决了重复调用构造函数的问题。

function Person(name) {this.name = name;
}
Person.prototype.getName = function() {return this.name;
}
function Student() {// 构造函数继承Person.apply(this, arguments)
}
// 原型式继承
// Student.prototype = new Person();
Student.prototype = Object.create(Person.prototype);// 原型的实例等于自身
Student.prototype.constructor = Student;const student = new Student('N-A');
console.log(student.name); // N-A
console.log(student.getName()); // N-A

它的基本原理是:

1. 使用 Object.create 创建一个临时的中间对象,这个对象的原型指向了父类的原型对象,但不直接执行父类的构造函数。

2. 将这个临时创建的对象赋值给子类的原型,这样子类的原型就可以继承自父类的原型,但避免了多余的构造函数调用。

这种继承方式在 ES5 中被认为是相对成熟且高效的继承方式,解决了组合继承中构造函数被执行两次的问题,保留了原型链继承和构造函数继承的优点,同时避免了它们的缺点。

Class继承

ES6 引入了类的概念,可以使用 classextends 关键字来实现类的继承。

class Person {constructor(name) {this.name = name;}greet() {return `Hello, my name is ${this.name}.`;}
}class Student extends Person {constructor(name, level) {super(name); // 调用父类构造函数来初始化父类的属性this.level = level;}study() {return `${this.name} studies at level ${this.level}.`;}
}// 创建一个 Student 实例
const student = new Student('N-A', 'Senior');console.log(student.greet()); // 输出:Hello, my name is N-A.
console.log(student.study()); // 输出:N-A studies at level Senior.

在这个示例中:

Person 类有一个 name 属性和一个 greet 方法。Student 类通过 extends 关键字继承了 Person 类,并在构造函数中使用 super() 调用父类的构造函数初始化父类的属性。

Student 类还有一个 study 方法。创建 Student 类的实例,并调用继承自父类和自身定义的方法。

类继承使得 JavaScript 中的继承更加直观和易用,同时也更符合传统面向对象编程的习惯。通过 extends 关键字,子类可以轻松地继承父类的属性和方法,并且可以在子类中添加自己的属性和方法。使用 super() 可以方便地调用父类构造函数,初始化父类的属性。

好啦,本文就到这里啦!

这篇关于编码遗传学:JavaScript 继承之道的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/492393

相关文章

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav