JavaScript是如何实现继承的(六种方式)

下面是 JavaScript 实现继承的六种方式的详细攻略:

1. 原型链继承

原型链继承是 JavaScript 实现继承最常用的方式之一。通过将子类的原型指向父类的实例,从而实现对父类属性和方法的继承。具体代码实现如下:

// 父类
function Person(name) {
  this.name = name;
}
Person.prototype.sayName = function () {
  console.log("My name is " + this.name + ".");
};

// 子类
function Student(name, grade) {
  this.grade = grade;
}
Student.prototype = new Person();

// 实例化子类
let s1 = new Student("Tom", "3");
s1.sayName(); // 输出 "My name is Tom."

这里我们将子类的原型 Student.prototype 指向了父类的实例 new Person(),这样子类的实例 s1 就可以直接访问父类 Person 的属性和方法了。

一个注意点是,子类自身定义的属性或方法会覆盖继承来的父类属性或方法,例如:

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

  this.sayName = function () {
    console.log("I am Student, my name is " + this.name + ".");
  };
}

let s2 = new Student("Tony", "5");
s2.sayName(); // 输出 "I am Student, my name is Tony."

上面的 sayName 方法覆盖了父类 Person 的同名方法。

2. 借用构造函数

借用构造函数是在子类构造函数内部通过 callapply 调用父类构造函数的方式实现继承。但是借用构造函数只能继承父类的属性,不能继承方法。具体代码实现如下:

// 父类
function Person(name) {
  this.name = name;
  this.sayName = function () {
    console.log("My name is " + this.name + ".");
  };
}

// 子类
function Student(name, grade) {
  Person.call(this, name); // 借用父类的构造函数初始化属性
  this.grade = grade;
}

// 实例化子类
let s3 = new Student("Jack", "2");
s3.sayName(); // 报错,父类的方法没有被继承
console.log(s3.name); // 输出 "Jack"

我们在子类的构造函数中通过 Person.call(this, name) 调用了父类 Person 的构造函数,从而实现对父类实例属性 name 的继承。但是由于没有继承父类的方法,所以调用 s3.sayName() 会报错。

3. 组合继承

组合继承是原型链继承和借用构造函数组合起来的方式。它通过原型链继承父类的方法和通过借用构造函数继承父类的属性两种方式结合来实现继承。具体代码实现如下:

// 父类
function Person(name) {
  this.name = name;
}
Person.prototype.sayName = function () {
  console.log("My name is " + this.name + ".");
};

// 子类
function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

Student.prototype = new Person(); // 原型链继承父类的方法
Student.prototype.constructor = Student; // 修复构造函数

// 实例化子类
let s4 = new Student("Mary", "1");
s4.sayName(); // 输出 "My name is Mary."
console.log(s4.name, s4.grade); // 输出 "Mary 1"

在这里我们既用到了原型链继承父类的方法,也通过借用构造函数继承了父类的属性,达到了全面继承的效果。

需要注意的是,由于子类的原型 Student.prototype 被重新赋值为了父类的实例 new Person(),所以需要额外修复一下子类的构造函数指向。

4. 原型式继承

原型式继承是通过指定一个基础对象来创建子类,实现对基础对象属性和方法的继承。具体代码实现如下:

// 基础对象
let baseObj = {
  name: "",
  sayName: function () {
    console.log("My name is " + this.name + ".");
  },
};

// 创建子类
let s5 = Object.create(baseObj);
s5.name = "Linda";

s5.sayName(); // 输出 "My name is Linda."
console.log(s5.name); // 输出 "Linda"

在这里我们用 Object.create 方法创建了一个新对象 s5,并指定了基础对象 baseObj 作为它的原型,从而实现了对基础对象的属性和方法的继承。

5. 寄生式继承

寄生式继承是在原型式继承的基础上增强了对象的功能。具体代码实现如下:

// 基础对象
let baseObj2 = {
  name: "",
  sayName: function () {
    console.log("My name is " + this.name + ".");
  },
};

// 增强对象的功能
let subObj = Object.create(baseObj2);
subObj.sayHi = function () {
  console.log("Hi, I am " + this.name + ".");
};

// 创建子类
let s6 = Object.create(subObj);
s6.name = "John";

s6.sayHi(); // 输出 "Hi, I am John."
console.log(s6.name); // 输出 "John"

在这里我们对原型式继承创建的对象 subObj 增加了一个 sayHi 方法,然后在它的基础上创建了新对象 s6。新对象依然继承了 baseObj2 的属性和方法,同时也有了自己的方法 sayHi

6. 寄生组合式继承

寄生组合式继承是在组合继承的基础上进行了优化,避免了父类构造函数被调用两次的问题。具体代码实现如下:

// 父类
function Person(name) {
  this.name = name;
}
Person.prototype.sayName = function () {
  console.log("My name is " + this.name + ".");
};

// 子类
function Student(name, grade) {
  Person.call(this, name); // 借用构造函数继承属性
  this.grade = grade;
}
Student.prototype = Object.create(Person.prototype); // 原型式继承方法
Student.prototype.constructor = Student; // 修复构造函数

// 实例化子类
let s7 = new Student("Lucy", "4");
s7.sayName(); // 输出 "My name is Lucy."
console.log(s7.name, s7.grade); // 输出 "Lucy 4"

在这里我们使用 Object.create 方法实现了对父类原型 Person.prototype 的继承,同时用 call 方法调用了父类构造函数,并且也修复了子类的构造函数。

以上就是 JavaScript 实现继承的六种方式的详细攻略和示例说明。希望能够对你有所帮助!

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript是如何实现继承的(六种方式) - Python技术站

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

相关文章

  • Python配置文件解析模块ConfigParser使用实例

    Python中内置了一个标准模块ConfigParser,该模块可以帮助开发者读取和解析常见的配置文件,如INI格式的文件。本文将详细讲解如何使用该模块来读取和解析INI文件。 安装ConfigParser ConfigParser是Python标准库中自带的模块,因此无需额外安装。 快速入门 首先,需要引入ConfigParser库: import con…

    other 2023年6月25日
    00
  • SpringBoot整合阿里云视频点播的过程详解

    下面是详细的Spring Boot整合阿里云视频点播的过程详解。 1. 创建阿里云账号并开通视频点播服务 首先需要创建一对阿里云的AccessKey ID和AccessKey Secret,以获取访问阿里云视频点播的权限。此外,还需要开通视频点播服务,获取点播服务的API地址。 2. 引入阿里云视频点播的SDK 在Spring Boot项目的pom.xml文…

    other 2023年6月27日
    00
  • 苹果发布iOS 10.2.1第2个测试版:版本号14D15

    苹果发布iOS 10.2.1第2个测试版:版本号14D15攻略 苹果公司最近发布了iOS 10.2.1的第2个测试版,版本号为14D15。这个测试版主要是为了让开发者和用户测试新功能和修复的bug。下面是详细的攻略,帮助你了解如何安装和使用这个测试版。 步骤1:备份你的设备 在安装任何测试版之前,强烈建议备份你的设备。这样,如果出现任何问题,你可以恢复到之前…

    other 2023年8月2日
    00
  • iOS 14.2修订版更新 固件内部版本号为18B111

    iOS 14.2修订版更新攻略 1. 简介 iOS 14.2修订版是苹果公司发布的最新操作系统版本,固件内部版本号为18B111。该版本修复了一些问题并引入了一些新功能和改进。本攻略将详细介绍如何更新到iOS 14.2修订版。 2. 更新前准备 在开始更新之前,请确保完成以下准备工作: 备份数据:在更新之前,建议您备份所有重要的数据,以防更新过程中出现意外情…

    other 2023年8月3日
    00
  • 作业二:Github注册账户过程

    作业二:Github注册账户过程 Github是一个非常流行的代码托管平台,它为全世界的程序员和开发者提供了一个高效的协作平台,无论是个人项目还是团队项目,都可以在Github上进行管理和分享。如果你还没有Github账户,那么接下来,我将向你介绍Github的注册过程。 注册Github账户的步骤 第一步:进入Github注册页面 首先,在浏览器中输入Gi…

    其他 2023年3月28日
    00
  • C++数组和指针的区别与联系

    C++ 数组和指针是 C++ 程序中经常使用的两种数据类型,很多初学者会混淆它们的用法和定义。本文将为您详细讲解 C++ 数组和指针的区别与联系,帮助您更好地理解和使用这两种数据类型。 C++ 数组和指针的定义 数组 C++ 数组是同类型元素的集合,这些元素存储在连续的内存位置中。数组可以是任何数据类型,如整型、字符型、浮点型等。数组中的每个元素可以通过下标…

    other 2023年6月27日
    00
  • 浅谈js中的变量名和函数名重名

    在JavaScript中,变量名和函数名可以重名,但这可能会导致一些问题。下面是一个详细的攻略,帮助您了解JavaScript中变量名和函数名重名的问题。 … 变量名和函数名重名的问题 当变量名和函数名重名时,可能会导致以下问题: 变量被函数覆盖:如果变量名和函数名重名,那么函数的定义将覆盖变量的值,导致无法访问原始变量的值。 函数调用错误:如果变量名和…

    other 2023年8月8日
    00
  • hex棋

    hex棋 什么是hex棋 hex棋是一种经典的棋类游戏,也是一种抽象策略游戏,是由丹麦数学家Piet Hein和美国数学家John Nash共同发明的。hex棋使用一个六边形的棋盘,两个玩家轮流下棋,每个玩家拥有不同颜色的棋子,棋子在棋盘上沿着棋子之间的线移动。游戏的目标是把自己的棋子连成一条线,从一侧到达对侧,与对手的棋子形成孤立。 hex棋的规则 hex…

    其他 2023年3月28日
    00
合作推广
合作推广
分享本页
返回顶部