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

yizhihongxing

下面就来详细讲解一下如何用原型链的方式实现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日

相关文章

  • SpringBoot注入配置文件的3种方法详解

    下面就详细讲解一下SpringBoot注入配置文件的3种方法。 方法1:使用@Value注解 步骤1:在application.properties配置文件中添加属性 app.name=My App app.version=1.0.0 步骤2:在代码中使用@Value注解进行注入 @RestController public class MyControll…

    other 2023年6月25日
    00
  • Java super关键字的用法详解

    Java super关键字的用法详解 简介 在Java中,有一个关键字叫作“super”,用于访问父类的属性和方法。它常常与子类的构造器和方法一起使用。本文将详细讲解Java super关键字的用法。 访问父类的属性和方法 子类继承父类后,可以访问到父类的公共(public)属性和方法。如果父类和子类定义了同名的属性和方法,那么子类访问的会是自己的属性和方法…

    other 2023年6月26日
    00
  • MySQL8.0.21.0社区版安装教程(图文详解)

    MySQL 8.0.21.0社区版安装教程(图文详解) MySQL是一款流行的开源关系型数据库管理系统,它被广泛用于Web应用程序的开发和管理。在本篇文章中,我们将介绍MySQL 8.0.21.0社区版的安装过程,并提供图文详解。 下载MySQL 8.0.21.0社区版 首先,我们需要下载MySQL 8.0.21.0社区版,可以在MySQL官网(https:…

    other 2023年6月27日
    00
  • 怎样对文件夹设置密码

    要对一个文件夹设置密码保护,可以采用以下步骤: 步骤一:创建压缩文件并设置密码 打开文件资源管理器,选中需要加密的文件夹。 右键点击选中的文件夹,选择“发送到” -> “压缩(zipped)文件夹”。 新建的压缩文件夹将出现在选中文件夹的旁边。右键点击它,选择“重命名”并将其名字改为你喜欢的名称。 右键点击新的压缩文件夹,选择“打开压缩文件夹”。 在弹…

    其他 2023年4月16日
    00
  • vue挂载元素的替换

    Vue挂载元素的替换 在Vue的开发中,我们经常需要动态地替换某一个元素,比如将一个标签替换成 标签,或者将一个 标签替换成标签等等。本文将介绍Vue中如何实现元素的替换。 使用v-if指令 Vue提供了一个非常方便的指令v-if,用于根据条件动态地控制元素的显示和隐藏。通过将待替换的元素和替换后的元素都分别放在两个<template>标签里,并…

    其他 2023年3月28日
    00
  • java数据结构和算法之马踏棋盘算法

    Java数据结构和算法之马踏棋盘算法 介绍 马踏棋盘算法是一种基于回溯算法实现的离散问题求解方法。它是将一只马放在棋盘任意指定的起始点,按照马的走法规则(“日”字形,即横向2格、纵向1格、或横向1格、纵向2格)依次跳到棋盘上的其它格子,直至棋盘所有格子都被访问并标记过。 方法 具体来说,算法的处理方法是从指定的起始格开始,按照一定的顺序依次尝试将马跳向下一个…

    other 2023年6月27日
    00
  • CentOS 5.1 4.6最新官方下载地址列表

    CentOS 5.1 4.6最新官方下载地址列表攻略 CentOS是一种基于Linux的操作系统,CentOS 5.1 4.6是其最新版本。在本攻略中,我们将详细讲解如何获取CentOS 5.1 4.6的官方下载地址列表。 步骤一:访问CentOS官方网站 首先,打开您的网络浏览器,并访问CentOS官方网站。您可以在以下网址找到官方网站:https://w…

    other 2023年8月4日
    00
  • JPA Specification常用查询+排序实例

    下面将详细讲解 JPA Specification 常用查询和排序的实现方法。 一、JPA Specification 查询实例 1. 前置条件 在使用 JPA Specification 进行查询前,需要先引入相关的依赖: <!– JPA规范,提供了一套标准API操作数据库 –> <dependency> <groupId…

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