发布于 2026-01-06 5 阅读
0

理解和使用 JavaScript 中的原型继承

理解和使用 JavaScript 中的原型继承

本文由 Ryan Thelin 撰写,他是 Educative.inc 的特约技术撰稿人。

JavaScript 是一种基于原型、面向对象的编程语言。ES6 更新后,JavaScript 引入了“原型继承”,这意味着对象和方法可以被共享、扩展和复制。这使得结构(数据字段)、行为(函数/方法)和状态(数据值)的继承变得非常容易。

JavaScript 是最常用的支持原型继承的语言,其功能也相对独特。如果使用得当,JavaScript 中的原型继承是一个强大的工具,可以节省大量的编码时间。

今天,我们将带您了解 JavaScript 中的原型继承,以便您及时掌握 ES6 的功能。

我们将涵盖以下内容:

什么是原型遗传?

替代文字

简而言之,原型继承是指能够从另一个对象访问其属性。我们使用JavaScript 原型向现有对象的构造函数添加新的属性和方法。然后,我们可以指示我们的 JS 代码从原型继承属性。原型继承允许我们通过引用指针函数将一个 JavaScript 对象中的属性或方法重用到另一个对象。

所有 JavaScript 对象都从原型继承属性和方法:

  • Date对象继承自Date.prototype
  • Array对象继承自Array.prototype
  • Player对象继承自Player.prototype

Object.prototype位于原型继承链的顶端。Date对象、Array对象和Player对象都继承自它Object.prototype

替代文字

图片来自dsinecos

重温旧例

让我们来看一个你可能在小学就熟悉的​​原型继承的例子:所有正方形都是长方形,但并非所有长方形都是正方形。如果我们把它想象成一个 JavaScript 程序,我们可以说长方形是正方形的原型:正方形继承了长方形的所有属性(即四条边和封闭),同时还增加了一个新特性(即所有边的长度都相等)。

但是,我们不能以正方形为原型来构建同样的概念,因为正方形有一些性质不适用于矩形(即所有边的长度都相同)。

我们可以通过指定组内类别(从最不具体到最具体,例如从矩形到正方形)来理解原型继承的工作原理。在代码中,这个概念有时会在语法中丢失。如果出现这种情况,请描述对象之间的关系,并注意区分的依据。如果您听到“所有___都是___,但并非所有___都是___”,那么就应该添加一个新的原型关系。

原型继承的缺点

原型继承对于 JavaScript 编程来说显然有很多好处,但就像所有工具一样,它也存在局限性。让我们来看看在编写基于原型的程序时需要注意的主要缺点:

  • 继承不能循环,否则会出错。例如,如果像上面的程序那样user将其链接premiumFamily为原型,就会产生循环,从而导致错误。

  • 对象不能继承自多个原型。正如我们上面看到的,它们可以通过链式继承多个对象的属性,但是显式地将另一个对象链接为原型会导致错误。即使额外的原型位于同一条链中,情况也是如此。例如,familyPremium不能同时显式地链接到 `A`premiumUser和 `B` user

  • 原型关系只能建立在对象之间。这是因为该__proto__函数充当转发器,引导程序找到它需要的值。由于程序要么知道在哪里查找,要么不知道,因此该函数只能是null对象或原型。所有其他类型都将被忽略。

重要术语

__proto__财产

在 JavaScript 中,每个对象都有其自身的隐藏内部属性[[Prototype]]。我们可以[[Prototype]]使用__proto__`isHidden` 属性来访问它。这会调用程序将模板对象标记为隐藏类型。JavaScript 对象必须链接到此原型对象。现在,继承对象就可以访问对象的属性了。

让我们来看一下访问和设置[[Prototype]]对象属性的语法。

//using __proto__ to access and set the [[Prototype]] of "anObject"
anObject.__proto__ = someotherObject
Enter fullscreen mode Exit fullscreen mode

对象.创建

JavaScript ECMAScript 5 引入了 `new` 函数Object.create( )。此方法可用于替代 `new` new。我们可以使用它基于已定义的原型创建一个空对象,然后将其赋值给另一个原型。以下是语法:

Object.create(proto, [propertiesObject])
Enter fullscreen mode Exit fullscreen mode

Object.create方法可以接受两个参数:propertiesObjectprototypeObject

对象.原型.构造函数

所有对象都有一个构造函数属性。如果对象是在未使用构造函数的情况下创建的,则它也会有一个构造函数属性。构造函数属性会返回对对象Object构造函数的引用1true1, and例如“test”。请看下面的示例。

替代文字

拥有房产

使用 `is` hasOwnProperty,我们可以测试一个对象是否包含某个原型属性;该方法会根据对象是否拥有该属性返回 `true`truefalse`false`。这将帮助你明确一个对象是拥有自己的属性,还是继承了其他属性。请查看以下语法:

js
obj.hasOwnProperty(prop)

原型链

原型继承利用了原型链的概念。让我们来探讨一下这个概念。每个创建的对象都包含一个 ` [[Prototype]][]`,它指向另一个对象或 `null`。设想一个对象 C,它有一个[[Prototype]]属性指向对象 B。对象 B 的 ` [[Prototype][]` 属性指向原型对象 A。以此类推,形成一种称为原型链的链。

这个概念用于代码搜索。当我们需要在对象中查找某个属性时,首先会在该对象内部查找,如果找不到,则会在该对象的原型中查找,依此类推。这样,整个原型链就会被遍历,直到找到或null到达目标属性为止。

想了解更多关于原型链式编程的内容吗?请查看“学习 JavaScript 中的面向对象编程”课程。

在接下来的章节中,我们将探讨一些在流媒体服务中使用帐户处理的实现方法。

示例代码 1:建立原型关系

在这个例子中,我们将使用函数编写两个对象 Auser和B 之间的简单原型关系。这两个对象各自拥有一些属性,这些属性在同一层级的所有帐户之间共享:所有帐户都可以访问流媒体节目A,并且所有帐户都禁用了广告。premiumUser._proto_usersshowAccess = truepremiumUsersads = false

替代文字

此处的原型关系确保了无需在高级层级手动设置,即可premiumUser继承showAccess属性集。为了检查是否已正确继承,我们添加一行代码,让控制台打印当前属性值。由于返回值为 0 ,我们可以看到已从继承了此属性usershowAccesspremiumUsertruepremiumUseruser

示例代码 2:继承方法

原型继承不仅可以用于从其他对象继承属性,还可以继承方法。在下面的示例中,我们基于之前的代码,为用户添加了email`and`IDnumber属性,用于跟踪该用户的帐户信息,以及一个 setter 方法。accountInfo该方法在被调用时会解析传入的字符串,email并将`and` 属性设置IDnumber为传入的新值。

替代文字

本示例的关键部分在于调用底部的三个方法。这些方法均定义在user对象内部,因此通常情况下无法被直接访问premiumUser。然而,由于user是的原型premiumUser,因此的所有方法和属性user都与所有继承对象共享。

从最后两种方法中,我们还可以看到,共享属性的值不是一成不变的,而是可以设置为新值,而不管原型中属性的值是什么。

示例代码 3:三层继承和可扩展性

如您所见,上述示例仅允许每个账户分别user位于不同的层级premiumUser。为了实现亟需的可扩展性,我们不再将这些对象用作变量,而是将其视为类。我们不再更改属性值,而是为每个账户创建新对象,并根据账户层级设置新对象的原型。

在下面的示例中,对象me是我的账户。该对象会调用继承的 setter 方法来设置此账户特有的属性值,并通过将新添加的对象设为原型来设置其层级email虽然这是一个使用单个账户对象的示例,但此过程可以扩展到为任意数量的对象分配正确的属性。IDnumberfamilyPremium

替代文字

即使继承层数达到三级,我们也可以看到,从直接继承的属性到位于继承链顶端的继承方法,它me都能访问整个继承链中的数据。无论继承链有多少层,之前所有层级的信息都会被保留并可访问。multipleDevicesaccountInfouser

通过使用原型继承,我们能够创建一个程序,只需几行代码即可添加新账户并为其分配既定属性,而无需手动设置。它还使得这些属性的调整更加便捷。如果我们只需更改原型中的属性,就能更改所有继承账户的属性,那就太好了。

因此,我们得到了一个可扩展、可适应且执行高效的程序,这一切都得益于基于原型的方法。

总结并举一个实际例子

与 JavaScript 本身一样,原型继承是网站开发和服务器管理领域中不可或缺的工具。具体来说,原型和原型继承常用于许多 Web 应用框架(例如 AngularJS),以实现相似组件之间共享通用行为和状态。

例如,AngularJS 使用层级结构scopes来继承数据值,以便进行数据绑定并在网页上显示。一个Site作用域对象可以定义网站的某些值,然后各个Title作用域对象可以使用原型链接来共享这些公共数据值。它还允许通过覆盖或增强特定页面的继承值来定制各个网页。IconImgPage

无论使用何种语法,无论是流媒体服务还是网页设计,原型继承都是所有 Web 应用程序开发项目的有用工具。

资源和延伸阅读

文章

基础课程

文章来源:https://dev.to/educative/understanding-and-using-prototypal-inheritance-in-javascript-5fo7