JavaScript ist eine prototypbasierte Sprache, und jedes Objekt in JavaScript verfügt über eine versteckte interne Eigenschaft namens [[Prototype]]
, mit der Objekteigenschaften und -methoden erweitert werden können. Weitere Informationen über Prototypen finden Sie in unserem Tutorial Prototypen und Vererbung in JavaScript verstehen.
Bis vor kurzem verwendeten fleißige Entwickler Konstruktorfunktionen, um ein objektorientiertes Entwurfsmuster in JavaScript nachzuahmen. Die Sprachspezifikation ECMAScript 2015, oft als ES6 bezeichnet, führte Klassen in die JavaScript-Sprache ein. Klassen in JavaScript bieten keine zusätzlichen Funktionen und werden häufig als „syntaktischer Zucker“ gegenüber Prototypen und Vererbung bezeichnet, da sie eine klarere und elegantere Syntax bieten. Da andere Programmiersprachen Klassen verwenden, erleichtert die Klassensyntax in JavaScript Entwicklern das Wechseln zwischen Sprachen.
Eine JavaScript-Klasse ist eine Art von Funktion. Klassen werden mit dem Schlüsselwort class
deklariert. Wir werden die Syntax von Funktionsausdrücken verwenden, um eine Funktion zu initialisieren, und die Syntax von Klassenausdrücken, um eine Klasse zu initialisieren.
// Initializing a function with a function expression
const x = function() {}
// Initializing a class with a class expression
const y = class {}
Mithilfe der Methode Object.getPrototypeOf()
können wir auf den [[Prototype]]
eines Objekts zugreifen. Damit werden wir jetzt die von uns erstellte leere Funktion testen.
Object.getPrototypeOf(x);
Outputƒ () { [native code] }
Wir können diese Methode auch für die Klasse verwenden, die wir gerade erstellt haben.
Object.getPrototypeOf(y);
Outputƒ () { [native code] }
Der mit function
und class
deklarierte Code gibt eine Funktion [[Prototype]]
zurück. Bei Prototypen kann jede Funktion durch Verwendung des Schlüsselworts new
zu einer Konstruktorinstanz werden.
const x = function() {}
// Initialize a constructor from a function
const constructorFromFunction = new x();
console.log(constructorFromFunction);
Outputx {}
constructor: ƒ ()
Dies gilt auch für Klassen.
const y = class {}
// Initialize a constructor from a class
const constructorFromClass = new y();
console.log(constructorFromClass);
Outputy {}
constructor: class
Diese Prototyp-Konstruktorbeispiele sind ansonsten leer, aber wir können sehen, wie unter der Syntax beide Methoden das gleiche Endergebnis erzielen.
In dem Tutorial Prototypen und Vererbung haben wir ein Beispiel erstellt, das auf der Charaktererstellung in einem textbasierten Rollenspiel basiert. Fahren wir hier mit diesem Beispiel fort, um die Syntax von Funktionen auf Klassen zu aktualisieren.
Eine Konstruktorfunktion wird mit einer Reihe von Parametern initialisiert, die als Eigenschaften von this
zugewiesen werden, was die Referenz auf die Funktion selbst darstellt. Der erste Buchstabe des Bezeichners wird entsprechend der Konvention großgeschrieben.
// Initializing a constructor function
function Hero(name, level) {
this.name = name;
this.level = level;
}
Wenn wir dies in die unten dargestellte class-Syntax übersetzen, sehen wir, dass sie sehr ähnlich strukturiert ist.
// Initializing a class definition
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
}
Anhand der Großschreibung des Anfangsbuchstabens des Initialisierers (optional) und durch die Kenntnis der Syntax wissen wir, dass eine Konstruktorfunktion ein Objektentwurf sein soll. Das Schlüsselwort class
kommuniziert auf einfachere Weise das Ziel unserer Funktion.
Der einzige Unterschied in der Syntax der Initialisierung besteht darin, das Schlüsselwort class
anstatt von function
zu verwenden und die Eigenschaften in einer constructor()
-Methode zuzuweisen.
Die übliche Vorgehensweise bei Konstruktorfunktionen besteht darin, Methoden anstatt in der Initialisierung direkt dem prototype
zuzuweisen, wie in der greet ()
-Methode unten dargestellt.
function Hero(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
Hero.prototype.greet = function() {
return `${this.name} says hello.`;
}
Mit Klassen wird diese Syntax vereinfacht, und die Methode kann direkt zur Klasse hinzugefügt werden. Unter Verwendung der in ES6 eingeführten Kurzform method definition ist das Definieren einer Methode ein noch präziserer Prozess.
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
greet() {
return `${this.name} says hello.`;
}
}
Sehen wir uns diese Eigenschaften und Methoden in Aktion an. Wir erstellen eine neue Instanz von Hero
mit dem Schlüsselwort new
und weisen einige Werte zu.
const hero1 = new Hero('Varg', 1);
Wenn wir mit console.log (hero1)
mehr Informationen über unser neues Objekt ausdrucken, können wir mehr Details dazu sehen, was mit der Klasseninitialisierung passiert.
OutputHero {name: "Varg", level: 1}
__proto__:
▶ constructor: class Hero
▶ greet: ƒ greet()
Aus der Ausgabe wird ersichtlich, dass die Funktionen constructor()
und greet()
auf __proto__
oder [[Prototype]
von hero1
angewendet wurden, nicht direkt als Methode für das Objekt hero1
. Während dies beim Erstellen von Konstruktorfunktionen klar ist, ist es beim Erstellen von Klassen nicht offensichtlich. Klassen ermöglichen eine einfachere und prägnantere Syntax; sie büßen jedoch an Klarheit ein.
Ein Vorteil von Konstruktorfunktionen und -klassen besteht darin, dass sie auf der Basis des übergeordneten Objekts zu neuen Objektentwürfen erweitert werden können. Dies verhindert die Wiederholung von Code für Objekte, die ähnlich sind, jedoch einige zusätzliche oder spezifischere Funktionen benötigen.
Neue Konstruktorfunktionen können vom übergeordneten Element mit der Methode call()
erstellt werden. Im nachstehenden Beispiel erstellen wir eine spezifischere Charakterklasse namens Mage
und weisen ihr die Eigenschaften von Hero
zu, indem wir call ()
verwenden. Außerdem fügen wir eine zusätzliche Eigenschaft hinzu.
// Creating a new constructor from the parent
function Mage(name, level, spell) {
// Chain constructor with call
Hero.call(this, name, level);
this.spell = spell;
}
Zu diesem Zeitpunkt können wir eine neue Instanz von Mage
erstellen, die dieselben Eigenschaften wie Hero
und die von uns neu hinzugefügte Eigenschaft verwendet.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Wenn wir hero2
an die Konsole senden, können wir sehen, dass wir einen neuen Mage
erstellt haben, der auf dem Konstruktor basiert.
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__:
▶ constructor: ƒ Mage(name, level, spell)
Bei ES6-Klassen wird das Schlüsselwort super
anstelle von call
verwendet, um auf die übergeordneten Funktionen zuzugreifen. Wir werden extends
verwenden, um auf die übergeordnete Klasse zu verweisen.
// Creating a new class from the parent
class Mage extends Hero {
constructor(name, level, spell) {
// Chain constructor with super
super(name, level);
// Add a new property
this.spell = spell;
}
}
Jetzt können wir in derselben Weise eine neue Mage
-Instanz erstellen.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Wir drucken hero2
auf der Konsole und zeigen die Ausgabe an.
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__: Hero
▶ constructor: class Mage
Die Ausgabe ist nahezu identisch, mit der Ausnahme, dass in der Klassenkonstruktion [[Prototype]]
mit dem übergeordneten Element verknüpft ist, in diesem Fall Hero
.
Nachstehend finden Sie einen direkten Vergleich des gesamten Prozesses der Initialisierung, des Hinzufügens von Methoden und der Vererbung einer Konstruktorfunktion und einer Klasse.
function Hero(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
Hero.prototype.greet = function() {
return `${this.name} says hello.`;
}
// Creating a new constructor from the parent
function Mage(name, level, spell) {
// Chain constructor with call
Hero.call(this, name, level);
this.spell = spell;
}
// Initializing a class
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
greet() {
return `${this.name} says hello.`;
}
}
// Creating a new class from the parent
class Mage extends Hero {
constructor(name, level, spell) {
// Chain constructor with super
super(name, level);
// Add a new property
this.spell = spell;
}
}
Obwohl die Syntax sehr unterschiedlich aussieht, ist das zugrundeliegende Ergebnis in beiden Methoden nahezu identisch. Klassen ermöglichen eine präzisere Erstellung von Objektentwürfen, und Konstruktorfunktionen beschreiben genauer, was unter der Haube geschieht.
In diesem Tutorial haben wir die Ähnlichkeiten und Unterschiede zwischen JavaScript-Konstruktorfunktionen und ES6-Klassen kennengelernt. Sowohl Klassen als auch Konstruktoren imitieren ein objektorientiertes Vererbungsmodell für JavaScript, eine prototypbasierte Vererbungssprache.
Das Verständnis der prototypischen Vererbung ist von größter Bedeutung, damit ein JavaScript-Entwickler effektiv arbeiten kann. Ein Verständnis von Klassen ist äußerst hilfreich, da gängige JavaScript-Bibliotheken wie React häufig die Syntax class
verwenden.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!