一文详解如何用原型链的方式实现JS继承

下面就来详细讲解一下如何用原型链的方式实现JS继承。

1. 什么是原型链

在JavaScript中,万物皆对象,每个对象都有 __proto__ 属性,指向了它的原型对象。原型对象也是一个对象,也有 __proto__ 属性,指向了它的原型对象。这样的对象构成了一个链状结构,被称为原型链。

2. 如何用原型链实现JS继承

原理很简单,就是在子类的原型对象上添加父类的实例作为子类的原型对象。

下面是一个例子,首先定义一个父类 Animal:

function Animal(name) {
  this.name = name;
}

Animal.prototype.say = function() {
  console.log(`我的名字是${this.name}`);
}

再定义一个子类 Dog,并将其原型对象指向 Animal 的实例:

function Dog(name, age) {
  Animal.call(this, name);
  this.age = age;
}

Dog.prototype = new Animal();

Dog.prototype.bark = function() {
  console.log(`我的名字是${this.name},我${this.age}岁了`);
}

Dog 构造函数中,调用 Animal 构造函数,使 Dog 实例具有 Animal 实例的属性。然后将 Dog 的原型对象指向 Animal 的实例,使 Dog 实例可以继承 Animal 实例的方法。

现在我们创建一个 Dog 的实例:

const dog = new Dog("旺财", 3);

然后调用 Dog 实例的 barksay 方法:

dog.bark();  // 我的名字是旺财,我3岁了
dog.say();   // 我的名字是旺财

这就是原型链实现 JS 继承的基本过程。

3. 原型链继承存在的问题

使用原型链继承虽然简单,但也存在一些问题:

  • 所有子类实例共享父类实例,无法使用父类传参
  • 对父类实例的属性修改会反映到所有子类实例上
  • 无法实现多继承

下面是一个示例,说明原型链继承存在上述问题:

function Person(name) {
  this.name = name;
  this.hobbies = [];
}

Person.prototype.say = function() {
  console.log(`我的名字是${this.name}`);
}

function Student(name, grade) {
  this.grade = grade;
}

Student.prototype = new Person();

const stu1 = new Student("小明", 3);
const stu2 = new Student("小红", 2);

stu1.hobbies.push("读书");
stu2.hobbies.push("画画");

console.log(stu1.hobbies); // 输出 [ '读书', '画画' ]
console.log(stu2.hobbies); // 输出 [ '读书', '画画' ]

由于 Person 类的原型实例被多个 Student 类实例共享,所以它们的 hobbies 属性会相互影响。

4. 如何解决原型链继承的问题

解决原型链继承的问题,可以有以下几种方法:

4.1 借用构造函数

使用 call 或 apply 方法,在子类构造函数中调用父类构造函数,这样就可以在子类实例上创建一个独立的属性副本。

function Animal(name) {
  this.name = name;
}

function Dog(name, age) {
  Animal.call(this, name);
  this.age = age;
}

const dog = new Dog("旺财", 3);
console.log(dog.name);  // 输出 旺财
console.log(dog.age);   // 输出 3

通过 call(this, ...) 将父类构造函数绑定到子类实例上,就可以在子类实例上创建一个独立的属性副本。

4.2 组合继承

组合继承就是将原型链继承和借用构造函数组合起来使用。首先使用原型链继承父类的原型对象,然后在子类构造函数中使用 call 或 apply 方法调用父类构造函数,以创建子类实例上的独立属性。

function Animal(name) {
  this.name = name;
}

Animal.prototype.say = function() {
  console.log(`我的名字是${this.name}`);
}

function Dog(name, age) {
  Animal.call(this, name);
  this.age = age;
}

Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log(`我的名字是${this.name},我${this.age}岁了`);
}

const dog = new Dog("旺财", 3);
console.log(dog.name);  // 输出 旺财
console.log(dog.age);   // 输出 3
dog.say();              // 输出 我的名字是旺财
dog.bark();             // 输出 我的名字是旺财,我3岁了

Dog.prototype = new Animal()Animal.call(this, name) 这两行组合起来就实现了组合继承。首先使用原型链继承父类的原型对象,然后在子类构造函数中调用父类构造函数,以创建子类实例上的独立属性。

4.3 原型式继承

使用 Object.create(proto) 方法,创建一个空对象,并将其原型对象指向一个对象。这样这个空对象就可以继承这个对象的方法和属性。

const animal = {
  say() {
    console.log(`我的名字是${this.name}`);
  }
}

const dog = Object.create(animal);
dog.name = "旺财";
dog.say();     // 输出 我的名字是旺财

在这个例子中 dog 对象的原型对象是 animal 对象,所以它可以调用 animal 对象的方法。

4.4 寄生式继承

寄生式继承其实是对原型式继承的一种封装。实现方法与原型式继承类似,不同的是需要在构造函数中返回一个实现了特定方法的对象。

function createDog(animal, name, age) {
  const dog = Object.create(animal);
  dog.name = name;
  dog.age = age;
  dog.bark = function() {
    console.log(`我的名字是${this.name},我${this.age}岁了`);
  }
  return dog;
}

const animal = {
  say() {
    console.log(`我的名字是${this.name}`);
  }
}

const dog = createDog(animal, "旺财", 3);
dog.say();   // 输出 我的名字是旺财
dog.bark();  // 输出 我的名字是旺财,我3岁了

createDog 函数中,使用 Object.create 方法创建一个空对象,并继承了 animal 对象的方法和属性,然后在这个空对象中添加独有的属性和方法,最后返回这个对象,以实现一种新的继承方式。

5. 总结

以上就是使用原型链实现 JS 继承的攻略,总的来说原型链继承是 JS 中最基本的继承方式之一,而组合继承则是最常用的继承方式之一。在实际开发中可以根据需求选择合适的继承方式,也可以结合多种方式来实现。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一文详解如何用原型链的方式实现JS继承 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 【转载】opencv实现人脸检测

    【转载】OpenCV实现人脸检测 人脸检测是计算机视觉领域中很常见的一个问题。OpenCV是一款可用于计算机视觉和机器学习的开源库,它可以帮助我们实现各种各样的视觉任务,其中就包括人脸检测。 OpenCV实现人脸检测的基本步骤 加载图像或视频,并用OpenCV提供的Haar Cascade分类器加载人脸数据集。 将图像或视频转换为灰度图像,因为Haar Ca…

    其他 2023年3月28日
    00
  • 右键菜单中新建“文本文档”消失了的解决办法

    问题描述: 当在右键菜单中新建文件时,没有“文本文档”选项。这通常发生在升级 Windows 系统或安装了第三方软件之后。 解决办法: 要恢复“文本文档”选项,可以按照以下步骤操作: Step 1:打开注册表编辑器 在开始菜单中搜索“regedit”,然后在搜索结果中选择“注册表编辑器”,打开注册表编辑器。如果系统提示需要管理员权限,选择“是”。 Step …

    other 2023年6月27日
    00
  • sqlserver 查询所有表及记录行数

    SQL Server查询所有表及记录行数 在SQL Server中,我们可以使用系统表来查询所有表及其记录行数。本文将介绍两种方法来查询所有表及其记录行数,并提供两个示例说明。 方法一:使用系统表 我们可以使用系统表sys.tables和sys.partitions来查询所有表及其记录行数。以下是一个示例: SELECT t.name AS TableNam…

    other 2023年5月7日
    00
  • Android之使用Android-query框架开发实战(一)

    针对题目中所提到的“Android之使用Android-query框架开发实战(一)”,我将为您详细讲解相关的完整攻略。请注意,以下的所有内容将按照规范的markdown格式进行展示。 什么是Android-query框架 Android-query是一个Android应用开发框架,它通过自定义的方式提供了一些简洁、灵活的api接口,让我们在开发过程中能够更…

    other 2023年6月27日
    00
  • 查看linux文件系统块大小的实现方法

    要查看Linux文件系统块大小,需要进行以下步骤: 第一步:确定当前使用的文件系统类型 可以使用df -T命令,查看当前挂载的文件系统类型,例如: df -T 输出结果可能类似于: Filesystem Type 1K-blocks Used Available Use% Mounted on /dev/sda1 ext4 220202936 2871360…

    other 2023年6月27日
    00
  • Angular网络请求的封装方法

    Angular是一种流行的前端框架,其能够帮助我们更好的构建Web应用程序。在开发过程中,我们需要与后端服务器进行通信,那么如何封装并使用网络请求呢?以下是一个完整的Angular网络请求的封装方法的攻略: 使用HttpClient Angular提供了一个HttpClient模块用于网络请求。首先,我们需要在我们的组件或服务中引入HttpClient: i…

    other 2023年6月25日
    00
  • 帝国cms所有的数据库表结构和字段说明

    下面是帝国 CMS 所有的数据库表结构和字段说明。 1. 表结构 1.1. 表 igg_attachment 该表存储所有的附件信息,包括附件的名称、大小、上传时间、存放路径等。 CREATE TABLE `igg_attachment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) …

    other 2023年6月25日
    00
  • Win 7 C盘瘦身的三个方法分享

    Win 7 C盘瘦身的三个方法分享 在Windows 7操作系统中,C盘是系统盘,存储着操作系统和程序文件。随着时间的推移,C盘可能会变得越来越拥挤,导致系统运行缓慢。为了解决这个问题,我们可以采取以下三个方法来瘦身C盘。 方法一:清理临时文件 Windows 7会在C盘上存储大量的临时文件,这些文件占据了宝贵的磁盘空间。清理这些临时文件可以帮助我们释放一些…

    other 2023年8月1日
    00
合作推广
合作推广
分享本页
返回顶部