JS继承与原型、原型链

在 JavaScript 中,继承是实现代码复用和构建对象关系的重要概念。本文将讨论原型链继承、构造函数继承以及组合继承等几种常见的继承方式,并提供相应的示例代码,并分析它们的特点、优缺点以及适用场景。

在开始讲解 JavaScript 的继承方式之前,我们先来详细解释一下原型(prototype)、构造函数(constructor)和实例对象(instance)这三个概念。因为只有对它们有清晰的理解,才能更好地理解和应用JavaScript的继承机制。现在我们将分别介绍它们的含义和它们之间的关系。

建议点赞收藏本文章,以便日后复习~~

原型、构造函数、实例对象

image.png

在 JavaScript 中,原型(prototype)、构造函数(constructor)和实例对象(instance)是面向对象编程中的重要概念,并且它们之间存在着紧密的关系。

  1. 原型(prototype): 原型是JavaScript中对象之间关联的一种机制。每个JavaScript对象(除了 null 和 undefined)都有一个原型对象,它包含了对象的属性和方法。当我们访问一个对象的属性或方法时,如果对象本身没有定义该属性或方法,JavaScript引擎会通过原型链向上查找,直到找到对应的属性或方法为止。同理,原型链是由对象的原型对象构成的链式结构,通过这种机制,对象可以继承原型对象的属性和方法。
  2. 构造函数(constructor): 构造函数是用于创建对象的函数。在JavaScript中,我们可以通过定义一个函数并使用 new 关键字来创建对象的实例。构造函数定义了对象的初始状态和行为,并且可以在创建实例时对其进行初始化。构造函数可以包含属性和方法,并且可以使用 this 关键字引用要创建的实例对象。
  3. 实例对象(instance): 实例对象是通过构造函数创建的对象,它具有构造函数定义的属性和方法。每个实例对象都是独立的,它们可以根据需要修改自己的属性值,并且可以调用构造函数中定义的方法。实例对象通过原型链与构造函数的原型对象关联在一起,从而实现属性和方法的继承。
function Animal(name) {  // 构造函数(自带原型对象)this.name = name || 'Animal'; // 属性
}Animal.prototype.eat = function(food) { // 方法console.log(this.name + '正在吃:' + food);
};var animal = new Animal('Tom'); // 实例对象animal.eat('猫粮')

它们之间的关系如下:

  • 每个构造函数都有一个原型对象(prototype),构造函数的原型对象包含了构造函数定义的属性方法
  • 通过构造函数创建的每个实例对象都有一个内部属性(Prototype),它指向构造函数的原型对象。这个属性在浏览器中通常可以通过 __proto__ 访问(某些浏览器不支持)。
  • 实例对象可以访问构造函数原型对象中的属性和方法。因为它们通过原型链与原型对象关联在一起,如果实例对象访问的属性或方法在自身中找不到,JavaScript引擎会自动沿着原型链向上查找,直到找到对应的属性或方法
  • 当我们修改实例对象的属性时,它会在自身中创建一个与原型对象中同名属性的副本,并在之后的访问中直接使用该副本,而不会影响原型对象中的属性
  • 当我们调用实例对象的方法时,JavaScript引擎会优先在实例对象中查找对应的方法,如果找不到则会继续沿着原型链向上查找,直到找到对应的方法。

什么是JavaScript继承

JavaScript继承是指在前端开发中,使用JavaScript实现对象之间属性和方法的继承关系。继承是面向对象编程的重要概念,它允许我们创建基于现有对象的新对象,并在新对象中拥有原有对象的属性和方法

在JavaScript中,继承是通过原型链来实现的。每个对象都有一个原型对象,它包含对象的属性和方法。当我们访问一个对象的属性或方法时,如果对象本身没有定义该属性或方法,JavaScript引擎会通过原型链向上查找,直到找到对应的属性或方法为止。这种原型链的查找机制使得对象之间可以共享属性和方法,从而实现继承的效果。

原型与对象的关系.jpeg

除了原型链继承外,JavaScript还提供了其他几种实现继承的方式,如构造函数继承、组合继承等。在开发中,继承通常用于构建对象之间的关系,使得子类可以共享父类的属性和方法,并有能力添加自己的特定属性和方法。这样可以减少重复的代码编写,提高开发效率和代码的可维护性。

接下来我们详细介绍这些继承方式的原理和使用方法,并提供相应的代码示例。

JavaScript继承方式

1、原型链继承

原型链继承是一种基于原型的继承方式,它通过将父类的实例作为子类的原型来实现继承关系。具体实现如下:

function Animal(name) {this.name = name || 'Animal';
}Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};function Cat() {Animal.call(this);
}Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.name = 'Tom';var cat = new Cat();

原型链继承的特点包括:

  • 实例既是子类的实例,也是父类的实例,继承关系非常纯粹。
  • 子类可以访问父类新增的原型方法和属性。
  • 实现简单,易于理解和实现。

原型链继承的缺点:

  • 子类无法在构造器中新增属性和方法,只能在实例化后添加。
  • 无法实现多继承。
  • 所有实例共享来自原型对象的属性,包括引用属性。

原型链继承适用于简单的继承关系和单一继承需求的场景。

2、构造继承

构造继承通过在子类构造函数中调用父类构造函数,复制父类的实例属性给子类。具体实现如下:

function Animal(name) {this.name = name || 'Animal';
}Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};function Cat(name) {Animal.call(this, name);
}var cat = new Cat();

构造继承的特点包括:

  • 解决了原型链继承中子类实例共享父类引用属性的问题。
  • 可以在创建子类实例时向父类传递参数。
  • 支持多继承,可以调用多个父类构造函数。

构造继承的缺点:

  • 子类实例并不是父类的实例,只是子类的实例。
  • 无法继承父类的原型属性和方法。
  • 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。

构造继承适用于需要继承实例属性、避免引用属性共享以及多继承的场景。

3、实例继承

实例继承通过为父类实例添加新特性,并将其作为子类实例返回。具体实现如下:

function Animal(name) {this.name = name || 'Animal';
}Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};function Cat(name) {var instance = new Animal();instance.name = name || 'Tom';return instance;
}var cat = new Cat();

实例继承的特点包括:

  • 不限制调用方式,无论是new 子类()还是子类(),返回的对象具有相同的效果。

实例继承的缺点:

  • 实例是父类的实例,而不是子类的实例。
  • 不支持多继承。

实例继承适用于灵活的对象创建需求,可以根据不同情况返回不同的实例。

4、拷贝继承

拷贝继承通过复制父类的属性和方法给子类实现继承。具体实现如下:

function Animal(name) {this.name = name || 'Animal';
}Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};function Cat(name) {Animal.call(this, name);
}for (var p in Animal.prototype) {if (Animal.prototype.hasOwnProperty(p)) {Cat.prototype[p] = Animal.prototype[p];}
}Cat.prototype.constructor = Cat;
var cat = new Cat();

拷贝继承的特点包括:

  • 支持多继承。

拷贝继承的缺点:

  • 效率较低,内存占用高,因为需要拷贝父类的属性。
  • 无法获取父类的不可枚举方法。

拷贝继承适用于多继承的场景,但要注意性能和不可枚举方法的问题。

5、组合继承(原型继承+构造继承)

组合继承结合了原型继承和构造继承的优点,通过调用父类构造函数来继承父类的属性,并将父类实例作为子类原型,实现函数复用。具体实现如下:

function Animal(name) {this.name = name || 'Animal';
}Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};function Cat(name) {Animal.call(this, name);
}Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;var cat = new Cat();

组合继承的特点包括:

  • 继承父类实例属性和方法。
  • 继承父类原型属性和方法。
  • 既是父类的实例,也是子类的实例。

组合继承的缺点:

  • 调用了两次父类构造函数,影响性能。

组合继承适用于大多数场景,是一种常用的继承方式。

6、寄生组合继承

寄生组合继承是对组合继承的优化,通过寄生方式避免了两次调用父类构造函数,从而减少了实例化时的重复操作。具体实现如下:

function Animal(name) {this.name = name || 'Animal';
}Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};function Cat(name) {Animal.call(this, name);
}function inheritPrototype(subType, superType) {var prototype = Object.create(superType.prototype);prototype.constructor = subType;subType.prototype = prototype;
}inheritPrototype(Cat, Animal);var cat = new Cat();

寄生组合继承的特点包括:

  • 实现简单,堪称完美。

寄生组合继承没有明显的缺点,是一种高效且可靠的继承方式。

结语

我们可以根据具体需求选择适合的继承方式。每种继承方式都有自己的优缺点和适用场景,因此我们应根据实际情况进行选择和灵活运用。

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

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

相关文章

从人机界面设计黄金三法则视角看 ChatGPT 的界面设计的“好”与“坏”

热门文章推荐: (1)《为什么很多人工作 3 年 却只有 1 年经验?》(2)《一文掌握大模型提示词技巧:从战略到战术巧》(3)《AI 时代,程序员的出路在何方&#xff1…

法向量估计

法向量估计 1. 求解点P法向量的原理2. 法向量估计的证明3. 为什么求点P的法向量,需要使用以P为中心的邻域内的点?4. 法向量估计的应用和思考5. 权重法向量估计 1. 求解点P法向量的原理 已知有一组点 P ( p 1 , p 2 , p 3 , . . . , p n ) , p i ∈ R 3…

小问题/小技巧/小知识汇总(记录贴)

目录 第一章、小问题①使用了Lombok的Data注解,但是getter方法失效,报错空指针异常③什么是字典项查询④workbench 导出包错误:could not get mysql version⑤一个自定义导包后出现的小问题⑥使用org.json将xml转json的时候出错⑦报错MySQLSy…

基于微信小程序的实验室预约系统的设计与开发

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

leetcode 热题 100(部分)C/C++

leetcode 热题 100 双指针 盛最多水的容器 【mid】【双指针】 思路: 好久没写代码sb了,加上之前写的双指针并不多,以及有点思维定势了。我对双指针比较刻板的印象一直是两层for循环i,j,初始时i,j都位于左界附近&…

GIS水文分析计算流向学习

1 初步操作 流向,即水文表面水的流向; 水文分析的很多功能需要基于流向栅格; 在 SuperMap 中,对中心栅格的8个邻域栅格进行编码; 每一个中心栅格的水流方向都由这八个值中的某一个值来确定; 我还没弄懂水…

深入理解计算机系统 家庭作业 2.83

要读懂题目挺难的 A. 假设我们要求的无穷串是x0.yyyyyyy... Y(0.y<<ky) (由YB2Uk(y)得到,B2Uk是一个截断成k位的函数) x0.yyyyy...(这是我们假设的) 于是有 Yx y.yyyyyy... Yx y.yyyyyy... x<<k Yx-x xY(-1) B. a.Y5 k3 ,x5/7 b.Y6 k4 ,x6/152/5 c…

基于单片机的盆栽自动浇花系统

**单片机设计介绍&#xff0c;基于单片机的盆栽自动浇花系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的盆栽自动浇花系统设计概要主要涵盖了通过单片机技术实现盆栽植物的自动化、智能化浇水管理。下面将从系统…

51之LCD1602与模块化编程

LCD1602&#xff0c;即我们开发板上附赠的那个液晶显示屏&#xff0c;我们通常可以使用这个液晶显示屏用来做调试工具&#xff0c;我们使用一下江科大提供的关于这个LCD1602的代码&#xff0c;用来为我们提供了类似C语言标准库里面的printf函数的用法&#xff0c;只是这个更加复…

深澜计费管理系统 任意文件读取漏洞复现

0x01 产品简介 深澜计费管理系统是是一套完善的领先的具有复杂生物型特征的弹性认证计费系统。系统主要由 AAA 认证计费平台、系统运营维护管理平台、用户及策略管理平台、用户自助服务平台、智能客户端模块、消息推送模块、数据统计模块组成。目前在全球为超过 2500 家客户提…

openEuler 22.03 SP3 安装图像桌面 UKUI

UKUI 是麒麟软件团队历经多年打造的一款 Linux 桌面&#xff0c;主要基于 GTK 和 QT 开发。与其他 UI 界面相比&#xff0c;UKUI 更加注重易用性和敏捷度&#xff0c;各元件相依性小&#xff0c;可以不依赖其他套件而独自运行&#xff0c;给用户带来亲切和高效的使用体验。 UK…

【Qt】文件

目录 一、输入输出设备类 二、文件读写类 三、文件和目录信息类 文件操作是应用程序必不可少的部分。Qt作为一个通用开发库&#xff0c;提供了跨平台的文件操作能力。Qt提供了很多关于文件的类&#xff0c;通过这些类能够对文件系统进行操作&#xff0c;如文件读写、文件信息…