Heim >Web-Frontend >js-Tutorial >Muster für die Objektvererbung in JavaScript ES2015

Muster für die Objektvererbung in JavaScript ES2015

Lisa Kudrow
Lisa KudrowOriginal
2025-02-16 11:38:39758Durchsuche

Muster für die Objektvererbung in JavaScript ES2015

Key Takeaways

  • Mit ES2015 verfügt JavaScript nun speziell mit Syntax, um Klassen zu definieren, um Code sauber zu halten, die Hierarchie -Tiefe zu minimieren und einen Duplikatcode zu vermeiden.
  • Multiple Vererbung, ein von einigen klassischer OOP -Sprachen unterstütztes Merkmal, ermöglicht die Erstellung einer Klasse, die von mehreren Basisklassen erbt. Es leidet jedoch unter dem Diamantproblem, bei dem zwei übergeordnete Klassen dieselbe Methode definieren.
  • Mixins, winzige Klassen, die nur Methoden enthalten, sind eine weitere Strategie, mit der dem Diamond -Problem ausgewählt wurde. Anstatt diese Klassen zu erweitern, sind Mixins in einer anderen Klasse enthalten.
  • Trotz der Klassensyntax, die der Illusion gibt, dass JavaScript eine klassenbasierte OOP-Sprache ist, ist dies nicht der Fall. Die meisten Ansätze erfordern das Ändern des Prototyps eines Objekts, um die Mehrfachvererbung nachzuahmen. Die Verwendung von Klassenfabrikfunktionen ist eine akzeptable Strategie für den Einsatz von Mixins zum Komponieren von Klassen.
Muster für die Objektvererbung in JavaScript ES2015

Mit der lang erwarteten Ankunft von ES2015 (früher als ES6 bekannt) ist JavaScript mit Syntax speziell zur Definition von Klassen ausgestattet. In diesem Artikel werde ich untersuchen, ob wir die Klassensyntax nutzen können, um Klassen aus kleineren Teilen zu komponieren.

Die Hierarchie -Tiefe auf ein Minimum hält, ist wichtig, um Ihren Code sauber zu halten. Wenn Sie klug darüber sind, wie Sie Klassen teilen, hilft. Für eine große Codebasis besteht eine Möglichkeit darin, Klassen aus kleineren Teilen zu erstellen. Klassen komponieren. Es ist auch eine häufige Strategie, um einen doppelten Code zu vermeiden.

Stellen Sie sich vor, wir bauen ein Spiel auf, in dem der Spieler in einer Welt der Tiere lebt. Einige sind Freunde, andere sind feindselig (ein Hund wie ich könnte sagen, dass alle Katzen feindliche Kreaturen sind). Wir könnten eine Klasse -Hostileanimal erstellen, die das Tier erweitert, um als Basisklasse für Katze zu dienen. Irgendwann entscheiden wir uns, Roboter hinzuzufügen, um Menschen Schaden zuzufügen. Das erste, was wir tun, ist die Roboterklasse zu erstellen. Wir haben jetzt zwei Klassen mit ähnlichen Eigenschaften. Sowohl hostileanimal als auch Roboter können beispielsweise angreifen ().

Wenn wir die Feindseligkeit in einer separaten Klasse oder einem Objekt irgendwie definieren könnten, können wir das für beide Katze als Roboter wiederverwenden. Wir können das auf verschiedene Arten tun.

Multiple Vererbung ist ein Merkmal, das einige klassische OOP -Sprachen unterstützen. Wie der Name schon sagt, gibt es uns die Fähigkeit, eine Klasse zu erstellen, die von mehreren Basisklassen erbt. Sehen Sie, wie die Katzenklasse im folgenden Python -Code mehrere Basisklassen erweitert:

<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>

an Schnittstelle ist eine gemeinsame Funktion in (typisierten) klassischen OOP -Sprachen. Es ermöglicht uns zu definieren, welche Methoden (und manchmal auch Eigenschaften) eine Klasse enthalten sollte. Wenn diese Klasse nicht der Fall ist, erhöht der Compiler einen Fehler. Der folgende TypeScript -Code würde einen Fehler aufwerfen, wenn CAT den Angriff () oder Walk () -Methoden nicht hätte:

<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>

Multiple Vererbung leidet unter dem Diamantproblem (bei dem zwei übergeordnete Klassen dieselbe Methode definieren). Einige Sprachen weichen diesem Problem aus, indem sie andere Strategien wie Mixins implementieren. Mixins sind winzige Klassen, die nur Methoden enthalten. Anstatt diese Klassen zu erweitern, sind Mixins in einer anderen Klasse enthalten. In PHP werden beispielsweise Mixins unter Verwendung von Merkmalen implementiert.

<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>

A RECAP: ES2015 Klasse Syntax

Wenn Sie nicht die Chance hatten, in ES2015-Klassen einzusteigen oder Sie nicht genug über sie zu wissen, lesen Sie Jeff Motts objektorientiertes JavaScript-ein tiefes Tauchgang in ES6-Klassen, bevor Sie fortfahren.

Kurz gesagt:

  • Klasse foo {...} beschreibt eine Klasse namens Foo
  • Klasse Foo erweitert die Bar {...} beschreibt eine Klasse, Foo, die eine andere Klasse erweitert, Bar

Innerhalb des Klassenblocks können wir Eigenschaften dieser Klasse definieren. Für diesen Artikel müssen wir nur Konstrukteure und Methoden verstehen:

  • constructor () {...} ist eine reservierte Funktion, die bei der Erstellung ausgeführt wird (New Foo ())
  • foo () {...} erstellt eine Methode namens Foo

Die Klassensyntax ist hauptsächlich syntaktischer Zucker über das Prototypmodell von JavaScript. Anstatt eine Klasse zu erstellen, wird ein Funktionskonstruktor erstellt:

<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>

Das Mitnehmen hier ist, dass JavaScript keine klassenbasierte OOP-Sprache ist. Man könnte sogar argumentieren, dass die Syntax täuscht und den Eindruck erweckt, dass es ist.

ES2015 -Klassen

komponieren

Schnittstellen können nachgeahmt werden, indem eine Dummy -Methode erstellt wird, die einen Fehler verursacht. Einmal geerbt, muss die Funktion überschrieben werden, um den Fehler zu vermeiden:

<span>class Foo {}
</span><span>console.log(typeof Foo); // "function"
</span>

Wie bereits vorgeschlagen, basiert dieser Ansatz auf der Vererbung. Um mehrere Klassen zu erben, benötigen wir entweder mehrere Vererbung oder Mixins.

Ein anderer Ansatz wäre, eine Dienstprogrammfunktion zu schreiben, die eine Klasse validiert, nachdem sie definiert wurde. Ein Beispiel hierfür findet sich ein Moment in der Wartezeit. JavaScript unterstützt die Mehrfachbeeinigung! Von Andrea Giammarchi. Siehe Abschnitt „Ein grundlegendes Objekt.implement -Funktionsprüfung.“

Zeit, um verschiedene Möglichkeiten zu untersuchen, um mehrere Vererbung und Mixins anzuwenden. Alle untersuchten Strategien unten sind auf Github verfügbar.

Object.Sign (ChildClass.Prototype, Mixin ...)

pre-ES2015 haben wir Prototypen zur Vererbung verwendet. Alle Funktionen haben eine Prototyp -Eigenschaft. Beim Erstellen einer Instanz mit neuem MyFunction () wird der Prototyp in der Instanz in eine Eigenschaft kopiert. Wenn Sie versuchen, auf eine Eigenschaft zuzugreifen, die sich nicht in der Instanz befindet, versucht die JavaScript -Engine, sie im Prototyp -Objekt nachzuschlagen.

Um demonstrieren, schauen Sie sich den folgenden Code an:

<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>

Diese Prototypobjekte können zur Laufzeit erstellt und geändert werden. Anfangs habe ich versucht, Klassen für Tier und feindlich zu verwenden:

<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>

Die oben genannte funktioniert nicht, da Klassenmethoden nicht aufzählbar sind. Praktisch bedeutet dies, dass Object.Sign (...) keine Methoden aus Klassen kopiert. Dies macht es auch schwierig, eine Funktion zu erstellen, die Methoden von einer Klasse zur anderen kopiert. Wir können jedoch jede Methode manuell kopieren:

<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>

Eine andere Möglichkeit besteht darin, Klassen auszugeben und Objekte als Mixins zu verwenden. Ein positiver Nebeneffekt ist, dass Mixinobjekte nicht verwendet werden können, um Instanzen zu erstellen, was Missbrauch verhindern kann.

<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

  • Mixins können nicht initialisiert werden

cons

  • erfordert eine zusätzliche Codezeile
  • Object.assisign () ist ein wenig dunkel
  • neu erfinden prototypischer Vererbung, um mit ES2015 -Klassen zu arbeiten

Objekte in Konstruktoren komponieren

Mit ES2015 -Klassen können Sie die Instanz überschreiben, indem Sie ein Objekt im Konstruktor zurückgeben:

<span>class Foo {}
</span><span>console.log(typeof Foo); // "function"
</span>

Wir können diese Funktion nutzen, um ein Objekt aus mehreren Klassen in einer Unterklasse zu komponieren. Beachten Sie, dass Object.Sign (...) immer noch nicht gut mit Mixin -Klassen funktioniert, also habe ich auch hier Objekte verwendet:

<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>

Da sich dies auf eine Klasse (mit nicht-erhöhten Methoden) im obigen Kontext bezieht, kopiert Object.Sign (..., dies) die Methoden der Katze nicht. Stattdessen müssen Sie Felder und Methoden auf dieser Ausdrücke einstellen, damit Object.assisign () in der Lage ist, diese wie so anzuwenden:

<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>

Dieser Ansatz ist nicht praktisch. Da Sie ein neues Objekt anstelle einer Instanz zurückgeben, entspricht es im Wesentlichen zu:

<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>

Ich denke, wir können zustimmen, dass letzteres lesbarer ist.

pros

  • es funktioniert, denke ich?

cons

  • sehr dunkel
  • Null profitieren von der ES2015 -Klasse Syntax
  • Missbrauch von ES2015 -Klassen

Klassenfabrikfunktion

Dieser Ansatz nutzt die Fähigkeit von JavaScript, eine Klasse zur Laufzeit zu definieren.

Erstens brauchen wir Basisklassen. In unserem Beispiel dienen Tier und Roboter als Basisklassen. Wenn Sie von vorne anfangen möchten, funktioniert auch eine leere Klasse.

<span>Object.assign(Cat.prototype, {
</span>  <span>attack: Hostile.prototype.attack,
</span>  <span>walk: Animal.prototype.walk,
</span><span>});
</span>

Als nächstes müssen wir eine Fabrikfunktion erstellen, die eine neue Klasse zurückgibt, die die Klassenbasis erweitert, die als Parameter übergeben wird. Dies sind die Mixins:

<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>

Jetzt können wir jede Klasse an die feindliche Funktion übergeben, die eine neue Klasse zurückgibt, in der feindliche und welche Klasse wir an die Funktion übergeben haben:

<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>

Wir könnten mehrere Klassen durchleiten, um mehrere Mixins anzuwenden:

<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>constructor() {
</span>    <span>// Cat-specific properties and methods go here
</span>    <span>// ...
</span>
    <span>return Object.assign(
</span>      <span>{},
</span>      <span>Animal,
</span>      <span>Hostile,
</span>      <span>this
</span>    <span>);
</span>  <span>}
</span><span>}
</span>
Sie können Objekt auch als Basisklasse verwenden:
<span>class Cat {
</span>  <span>constructor() {
</span>    <span>this.purr = () => {
</span>      <span>// ...
</span>    <span>};
</span>
    <span>return Object.assign(
</span>      <span>{},
</span>      <span>Animal,
</span>      <span>Hostile,
</span>      <span>this
</span>    <span>);
</span>  <span>}
</span><span>}
</span>

pros

  • einfacher zu verstehen, da alle Informationen in der Klassenerklärung -Header
  • enthalten sind

cons
  • Erstellen von Klassen zur Laufzeit kann sich auf die Startleistung und/oder Speicherverwendung auswirken

Schlussfolgerung

Als ich mich entschied, dieses Thema zu erforschen und einen Artikel darüber zu schreiben, erwartete ich, dass das prototypische Modell von JavaScript hilfreich für die Generierung von Klassen ist. Da die Klassensyntax die Methoden nicht erhöht, wird die Objektmanipulation viel schwieriger, fast unpraktisch.

Die Klassensyntax könnte die Illusion erzeugen, dass JavaScript eine klassenbasierte OOP-Sprache ist, dies jedoch nicht der Fall ist. Bei den meisten Ansätzen müssen Sie den Prototyp eines Objekts ändern, um die Mehrfachvererbung nachzuahmen. Der letzte Ansatz unter Verwendung von Klassenfabrikfunktionen ist eine akzeptable Strategie für die Verwendung von Mixins zum Komponieren von Klassen.

Wenn Sie prototypbasiertes Programmieren restriktiv finden, sollten Sie sich Ihre Denkweise ansehen. Prototypen bieten beispiellose Flexibilität, die Sie nutzen können.

Wenn Sie aus irgendeinem Grund immer noch klassische Programmierung bevorzugen, möchten Sie möglicherweise Sprachen prüfen, die mit JavaScript kompilieren. Typscript ist beispielsweise ein Superet von JavaScript, das (optionale) statische Typisierung und Muster hinzufügt, die Sie aus anderen klassischen OOP -Sprachen erkennen.

Wirst du einen der oben genannten Ansätze in deinen Projekten verwenden? Haben Sie bessere Ansätze gefunden? Lass es mich in den Kommentaren wissen!

Dieser Artikel wurde Peer von Jeff Mott, Scott Molinari, Vildan Softic und Joan Yin bewertet. Vielen Dank an alle Peer -Rezensenten von SitePoint, die SitePoint -Inhalte so gut wie möglich gemacht haben!

häufig gestellte Fragen zu JavaScript ES2015 -Objektvererbranz

Was ist der Unterschied zwischen klassischer und prototypischer Vererbung in JavaScript? Eine Klasse definiert eine Blaupause für ein Objekt und Objekte sind Fälle einer Klasse. Die Vererbung wird durch Erstellen von Unterklassen aus einer Superklasse erreicht. Andererseits verwendet JavaScript eine prototypische Vererbung, bei der Objekte direkt von anderen Objekten erben. Dies ist flexibler, da Objekte zur Laufzeit dynamisch erweitert werden können. Rufen Sie Funktionen auf dem übergeordneten Objekt an. Bei Verwendung in einem Konstruktor erscheint das Schlüsselwort "Super" allein und muss verwendet werden, bevor das Schlüsselwort "This" verwendet wird. Das Schlüsselwort "Super" kann auch verwendet werden, um Funktionen auf einem übergeordneten Objekt in Methoden aufzurufen.

Was ist der „Prototyp“ in JavaScript und wie wird er in der Vererbung verwendet? Diese Eigenschaft ist ein Verweis auf ein anderes Objekt, das Prototypobjekt. Wenn eine Funktion erstellt wird, wird ihr Prototypobjekt auch über die Prototyp -Eigenschaft der Funktion erstellt und verknüpft. Wenn ein Objekt unter Verwendung einer Konstruktorfunktion erstellt wird, erbt es die Eigenschaften und Methoden aus dem Prototyp des Konstruktors. Es kann jedoch indirekt unter Verwendung von Mixins erreicht werden. Ein Mixin ist eine Technik, bei der Eigenschaften von einem Objekt zum anderen kopiert werden. Dies ermöglicht es einem Objekt, Eigenschaften und Methoden aus mehreren Quellen zu erben. Spezielle Methode zum Erstellen und Initialisieren eines Objekts innerhalb einer Klasse. Im Kontext der Vererbung muss ein Unterklassenkonstruktor den Superklassenkonstruktor mit dem Schlüsselwort "Super" aufrufen, bevor er das Schlüsselwort "This" verwenden kann. >

Das Schlüsselwort 'erweitert' in JavaScript ES2015 wird verwendet, um eine Unterklasse aus einer Superklasse zu erstellen. Die Unterklasse erbt alle Eigenschaften und Methoden der Superklasse, kann aber auch neue hinzufügen oder die ererbten überschreiben. > In JavaScript ist eine "Klasse" eine Art von Funktion, die als Blaupause verwendet wird, um Objekte zu erstellen. Es fasst Daten und Funktionen zusammen, die diese Daten betreiben. Andererseits ist ein "Prototyp" ein Objekt, aus dem andere Objekte Eigenschaften und Methoden erben. Sie können eine Methode in einer Unterklasse überschreiben, indem Sie einfach eine Methode mit demselben Namen in der Unterklasse definieren. Die neue Methode wird anstelle der Ererben verwendet, wenn sie auf eine Instanz der Unterklasse aufgerufen wird. > Mit dem Schlüsselwort "neu" in JavaScript wird eine Instanz einer Klasse oder einer Konstruktorfunktion erstellt. Im Kontext der Vererbung wird das 'neue' Schlüsselwort verwendet, um eine Instanz einer Unterklasse zu erstellen, die Eigenschaften und Methoden aus einer Superklasse erbt. >

In JavaScript können Sie dem Prototyp eines Objekts eine Eigenschaft hinzufügen, indem Sie einfach einer Eigenschaft im Prototyp -Objekt einen Wert zuweisen. Diese Eigenschaft wird dann von allen aus der Konstruktorfunktion erstellten Objekte vererbt.

Das obige ist der detaillierte Inhalt vonMuster für die Objektvererbung in JavaScript ES2015. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn