首页 >web前端 >js教程 >JavaScript ES2015中对象继承的模式

JavaScript ES2015中对象继承的模式

Lisa Kudrow
Lisa Kudrow原创
2025-02-16 11:38:39755浏览

JavaScript ES2015中对象继承的模式

钥匙要点

  • >使用ES2015,JavaScript现在具有专门定义类的语法,以保持代码清洁,最小化层次结构深度并避免重复代码。
  • >多重继承(由某些经典OOP语言支持的功能)允许创建从多个基类继承的类。但是,它遇到了钻石问题,其中两个父类定义了相同的方法。
  • 混合蛋白,仅包含方法的微小类是躲避钻石问题的另一种策略。 Mixins不用扩展这些类,而是在另一个类中包含。
  • >
  • >尽管班级语法给出了一种幻想,即JavaScript是一种基于类的OOP语言,但事实并非如此。大多数方法都需要修改对象的原型以模仿多重继承。使用类工厂功能是使用Mixins组成类的可接受策略。
  • JavaScript的
  • > ES2015(以前称为ES6)的已久的到来,配备了专门定义类的语法。在本文中,我将探讨是否可以利用类语法来撰写较小零件的类。
JavaScript ES2015中对象继承的模式>将层次深度保持在最低限度对于保持代码清洁很重要。对您如何拆分课程的方式很聪明。对于大型代码库,一种选择是创建较小零件的类。撰写课程。这也是避免重复代码的常见策略。

想象我们正在建立一个玩家生活在动物世界中的游戏。有些是朋友,有些是敌对的(像我这样的狗人可能会说所有猫都是敌对的生物)。我们可以创建一个hostileanimal,它可以扩展动物,以充当猫的基础。在某个时候,我们决定添加旨在伤害人类的机器人。我们要做的第一件事是创建机器人类。现在,我们有两个具有相似属性的类。例如,hostileanimal和机器人都可以攻击()。

>

>如果我们可以以某种方式在单独的类或对象中定义敌意,请说敌对,我们可以将其重用为CAT作为机器人。我们可以以各种方式做到这一点。

>多重继承

是某些经典的OOP语言支持的功能。顾名思义,它使我们能够创建一个从多个基类继承的类。查看CAT类在以下Python代码中如何扩展多个基类:>

and接口

是(键入)经典OOP语言中的常见功能。它允许我们定义类应包含的方法(有时是属性)。如果该课程没有,编译器将引起错误。如果CAT没有攻击()或walk()方法:>
<span>class Animal(object):
</span>  <span>def walk(self):
</span>    <span># ...
</span>
<span>class Hostile(object):
</span>  <span>def attack(self, target):
</span>    <span># ...
</span>
<span>class Dog(Animal):
</span>  <span># ...
</span>
<span>class Cat(Animal, Hostile):
</span>  <span># ...
</span>
dave <span>= Cat();
</span>dave<span>.walk();
</span>dave<span>.attack(target);
</span>
多个继承遭受钻石问题(其中两个父类定义相同的方法)。某些语言通过实施其他策略(例如)来避免此问题。 混合素是仅包含方法的小类。 Mixin并没有扩展这些课程,而是将Mixins包含在另一个类中。例如,在PHP中,使用特征实现Mixin。

a recap:es2015类语法

>如果您没有机会参加ES2015课程或觉得自己对它们的了解不足,请务必阅读Jeff Mott面向对象的JavaScript - 在继续之前深入研究ES6课程。 > 简而言之:
<span>interface Hostile {
</span>  <span>attack();
</span><span>}
</span>
<span>class Animal {
</span>  <span>walk();
</span><span>}
</span>
<span>class Dog extends Animal {
</span>  <span>// ...
</span><span>}
</span>
<span>class Cat extends Animal implements Hostile {
</span>  <span>attack() {
</span>    <span>// ...
</span>  <span>}
</span><span>}
</span>

>类foo {...}描述了一个名为foo

的类

类foo扩展bar {...}描述了一个foo的类,该类扩展了另一个类,bar

>

在类块中,我们可以定义该类的属性。在本文中,我们只需要了解构造函数和方法:
  • 构建器(){...}是一个保留函数,在创建时执行(new foo())>
  • > foo(){...}创建一个名为foo
  • 的方法

级语法主要是JavaScript原型模型上的句法糖。它不是创建类,而是创建一个函数构造函数:

>
  • 的要点是,JavaScript不是基于类的OOP语言。甚至可能认为语法是欺骗性的,给人的印象是。
  • 组成ES2015类
  • 可以通过创建一个抛出错误的虚拟方法来模仿
  • 接口。一旦继承,就必须覆盖该功能以避免错误:

如前所述,这种方法依赖于继承。要继承多个类,我们将需要多个继承或混合素。

<span>class Animal {
</span>  <span>// ...
</span><span>}
</span>
<span>trait Hostile {
</span>  <span>// ...
</span><span>}
</span>
<span>class Dog extends Animal {
</span>  <span>// ...
</span><span>}
</span>
<span>class Cat extends Animal {
</span>  <span>use Hostile;
</span>  <span>// ...
</span><span>}
</span>
<span>class Robot {
</span>  <span>use Hostile;
</span>  <span>// ...
</span><span>}
</span>
>另一种方法是编写一个实用程序函数,该函数在定义后验证类。可以在等待时找到一个例子,JavaScript确实支持多个继承!由Andrea Giammarchi。请参阅“基本对象”部分。

>探索应用多种继承和混合素的各种方法的时间。以下所有检查的策略均可在GitHub上获得。

>

object.assign(childclass.protype,mixin ...)

> ES2015前,我们使用原型来继承。所有功能都有一个原型属性。使用新的myFunction()创建实例时,将原型复制到实例中的属性。当您尝试访问不在实例的属性时,JavaScript引擎将尝试在原型对象中查找。

进行演示,请查看以下代码:
<span>class Foo {}
</span><span>console.log(typeof Foo); // "function"
</span>
>

可以在运行时创建和修改这些原型对象。最初,我试图将课程用于动物和敌对:

<span>class Animal(object):
</span>  <span>def walk(self):
</span>    <span># ...
</span>
<span>class Hostile(object):
</span>  <span>def attack(self, target):
</span>    <span># ...
</span>
<span>class Dog(Animal):
</span>  <span># ...
</span>
<span>class Cat(Animal, Hostile):
</span>  <span># ...
</span>
dave <span>= Cat();
</span>dave<span>.walk();
</span>dave<span>.attack(target);
</span>

上面的方法是不起作用的,因为类方法是>不枚举的。实际上,这意味着对象。分配(...)不从类复制方法。这也使得很难创建一个将方法从一个类复制到另一个类的函数。但是,我们可以手动复制每种方法:

>
<span>interface Hostile {
</span>  <span>attack();
</span><span>}
</span>
<span>class Animal {
</span>  <span>walk();
</span><span>}
</span>
<span>class Dog extends Animal {
</span>  <span>// ...
</span><span>}
</span>
<span>class Cat extends Animal implements Hostile {
</span>  <span>attack() {
</span>    <span>// ...
</span>  <span>}
</span><span>}
</span>

>另一种方法是抛弃类并使用对象作为混合物。积极的副作用是Mixin对象不能用于创建实例,以防止滥用。

<span>class Animal {
</span>  <span>// ...
</span><span>}
</span>
<span>trait Hostile {
</span>  <span>// ...
</span><span>}
</span>
<span>class Dog extends Animal {
</span>  <span>// ...
</span><span>}
</span>
<span>class Cat extends Animal {
</span>  <span>use Hostile;
</span>  <span>// ...
</span><span>}
</span>
<span>class Robot {
</span>  <span>use Hostile;
</span>  <span>// ...
</span><span>}
</span>
pros

    混合蛋白不能初始化
  • >
cons

需要额外的代码
  • object.assign()有点晦涩
  • 与ES2015类合作的重塑原型继承
  • 在构造函数中编写对象
  • >
>使用ES2015类,您可以通过在构造函数中返回对象来覆盖实例:

>我们可以利用该功能从子类内部的多个类中构成对象。请注意,object.sign(...)仍然与Mixin类无法正常工作,因此我在这里也使用了对象:>

<span>class Foo {}
</span><span>console.log(typeof Foo); // "function"
</span>
因为这是指在上下文中的类(使用不可算的方法),所以object.shassign(...,this)不复制cat的方法。相反,您必须在此上明确设置此字段和方法,以便object.Assign()能够应用这些字段和方法,例如:

>

<span>class IAnimal {
</span>  <span>walk() {
</span>    <span>throw new Error('Not implemented');
</span>  <span>}
</span><span>}
</span>
<span>class Dog extends IAnimal {
</span>  <span>// ...
</span><span>}
</span>
<span>const robbie = new Dog();
</span>robbie<span>.walk(); // Throws an error
</span>
这种方法是不切实际的。因为您要返回一个新对象而不是实例,所以它基本上等同于:>

我认为我们可以同意后者更可读。
<span>function <span>MyFunction</span> () {
</span>  <span>this.myOwnProperty = 1;
</span><span>}
</span><span>MyFunction.prototype.myProtoProperty = 2;
</span>
<span>const myInstance = new MyFunction();
</span>
<span>// logs "1"
</span><span>console.log(myInstance.myOwnProperty);
</span><span>// logs "2"
</span><span>console.log(myInstance.myProtoProperty);
</span>
<span>// logs "true", because "myOwnProperty" is a property of "myInstance"
</span><span>console.log(myInstance.hasOwnProperty('myOwnProperty'));
</span><span>// logs "false", because "myProtoProperty" isn’t a property of "myInstance", but "myInstance.__proto__"
</span><span>console.log(myInstance.hasOwnProperty('myProtoProperty'));
</span>
>

pros

<span>class Animal {
</span>  <span>walk() {
</span>    <span>// ...
</span>  <span>}
</span><span>}
</span>
<span>class Dog {
</span>  <span>// ...
</span><span>}
</span>
<span>Object.assign(Dog.prototype, Animal.prototype);
</span>

>它有效,我猜

>

cons
  • 非常模糊的
  • ES2015类语法
零收益

滥用ES2015类
  • 类工厂功能
  • 这种方法利用JavaScript在运行时定义类的能力。
  • 首先,我们需要基础类。在我们的例子中,动物和机器人充当基础类别。如果您想从头开始,一个空的班级也可以。
  • >
接下来,我们必须创建一个返回新类的工厂功能,该函数扩展了类基础,该类作为参数传递。这些是混合物:

>现在,我们可以将任何类传递给敌对功能,该函数将返回一个新的类,结合敌对的新类,以及我们传递给该功能的任何类:>

我们可以通过几个类进行管道应用多个混合素:

<span>Object.assign(Cat.prototype, {
</span>  <span>attack: Hostile.prototype.attack,
</span>  <span>walk: Animal.prototype.walk,
</span><span>});
</span>
您还可以将对象用作基类:

pros
<span>const Animal = {
</span>  <span>walk() {
</span>    <span>// ...
</span>  <span>},
</span><span>};
</span>
<span>const Hostile = {
</span>  <span>attack(target) {
</span>    <span>// ...
</span>  <span>},
</span><span>};
</span>
<span>class Cat {
</span>  <span>// ...
</span><span>}
</span>
<span>Object.assign(Cat.prototype, Animal, Hostile);
</span>

>更容易理解,因为所有信息都在类声明标题中
<span>class Answer {
</span>  <span>constructor(question) {
</span>    <span>return {
</span>      <span>answer: 42,
</span>    <span>};
</span>  <span>}
</span><span>}
</span>
<span>// { answer: 42 }
</span><span>new Answer("Life, the universe, and everything");
</span>

cons

  • 在运行时创建类可能会影响启动性能和/或内存使用

结论

>当我决定研究此主题并撰写有关它的文章时,我希望JavaScript的原型模型有助于生成课程。由于类语法使方法无法恢复,因此对象操纵变得更加困难,几乎不切实际。

类语法可能会产生幻想,即JavaScript是一种基于类的OOP语言,但事实并非如此。使用大多数方法,您将必须修改对象的原型以模仿多重继承。使用类工厂功能的最后一种方法是使用Mixins组成类的可接受策略。

>如果发现基于原型的编程限制性,则可能需要查看自己的心态。原型提供了无与伦比的灵活性。

如果出于任何原因,您仍然喜欢经典的编程,则可能需要研究编译为JavaScript的语言。例如,打字稿是JavaScript的超集,它添加了(可选的)静态键入和您将从其他经典OOP语言中识别的模式。>

您是否要在项目中使用上述两种方法?您找到了更好的方法吗?在评论中让我知道!

>

>本文是由杰夫·莫特(Jeff Mott),斯科特·莫利纳里(Scott Molinari),维尔丹·苏菲奇(Vildan Softic)和琼·元(Joan Yin)进行了审查。感谢SitePoint所有的同行评审器制作SitePoint内容的最佳功能!

javaScript ES2015对象继承经常询问问题 >在JavaScript中的经典和原型继承之间有什么区别?一个类定义一个对象的蓝图,对象是类的实例。继承是通过从超类创建子类来实现的。另一方面,JavaScript使用原型继承,其中对象直接从其他对象继承。这是更灵活的,因为可以在运行时扩展或动态更改对象。

>

>'超级'关键字在JavaScript ES2015中如何工作?在对象的父上调用功能。当在构造函数中使用时,“超级”关键字单独出现,必须在使用“此”关键字之前使用。 “超级”关键字也可用于在方法中的父对象上调用函数。>

> JavaScript中的“原型”是什么?在JavaScript中如何使用它在继承中?此属性是对另一个对象的原型对象的引用。创建函数时,其原型对象也会通过函数的原型属性创建并链接。当使用构造函数函数创建对象时,它从其构造函数的原型中继承了属性和方法。

> javaScript如何处理多个继承?

javaScript不直接支持多个继承。但是,可以使用混合蛋白间接实现它。混合蛋白是一种涉及将属性从一个对象复制到另一个对象的技术。这允许对象从多个来源继承属性和方法。

>

> javaScript ES2015中的'构造函数是什么?在javascript ES2015中如何使用它?用于创建和初始化类中的对象的特殊方法。在继承的上下文中,子类构造函数必须使用“超级”关键字调用超级类构造函数,然后才能使用'this'关键字。 >

JavaScript ES2015中的“扩展”关键字用于从超级类创建子类。子类继承了超类的所有属性和方法,但也可以添加新的属性或覆盖继承的属性。 >在JavaScript中,“类”是一种用作创建对象的蓝图的函数。它封装了在该数据上运行的数据和功能。另一方面,“原型”是一个对象,其他对象从中继承属性和方法。

>

>我如何在JavaScript ES2015中的子类中覆盖一个方法?您可以通过简单地定义子类中具有相同名称的方法来覆盖子类中的方法。新方法将在子类的实例上调用。 > JavaScript中的“新”关键字用于创建类或构造函数函数的实例。在继承的上下文中,“新”关键字用于创建一个子类的实例,该子类从超级类继承属性和方法。

如何在JavaScript中添加属性? >

>在JavaScript中,您可以通过简单地将值分配给原型对象上的属性来将属性添加到对象的原型中。然后,此属性将由从构造函数函数创建的所有对象继承。

>

以上是JavaScript ES2015中对象继承的模式的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn