Einführung
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 zu Prototypen finden Sie in unserem Tutorial zuUnderstanding Prototypes and Inheritance in JavaScript.
Bis vor kurzem verwendeten fleißige Entwicklerconstructor functions, 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ätzliche Funktionalität und werden oft 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.
Klassen sind Funktionen
Eine JavaScript-Klasse ist eine Art von Funktion. Klassen werden mit dem Schlüsselwortclass
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 {}
Mit denObject.getPrototypeOf()
method können wir auf die[[Prototype]]
eines Objekts zugreifen. Verwenden wir dies, um die leerenfunctionzu testen, die wir erstellt haben.
Object.getPrototypeOf(x);
Outputƒ () { [native code] }
Wir können diese Methode auch für dieclassverwenden, die wir gerade erstellt haben.
Object.getPrototypeOf(y);
Outputƒ () { [native code] }
Der mitfunction
undclass
deklarierte Code gibt beide eine Funktion[[Prototype]]
zurück. Bei Prototypen kann jede Funktion mithilfe des Schlüsselwortsnew
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 den Unterricht.
const y = class {}
// Initialize a constructor from a class
const constructorFromClass = new y();
console.log(constructorFromClass);
Outputy {}
constructor: class
Diese Beispiele für Prototypkonstruktoren sind ansonsten leer, aber wir können sehen, dass unter der Syntax beide Methoden das gleiche Endergebnis erzielen.
Eine Klasse definieren
Inprototypes and inheritance tutorial haben wir ein Beispiel für die Charaktererstellung in einem textbasierten Rollenspiel erstellt. Fahren wir hier mit diesem Beispiel fort, um die Syntax von Funktionen zu Klassen zu aktualisieren.
Einconstructor function wird mit einer Reihe von Parametern initialisiert, die als Eigenschaften vonthis
zugewiesen werden und sich auf die Funktion selbst beziehen. Der erste Buchstabe des Bezeichners wird nach Konvention großgeschrieben.
constructor.js
// Initializing a constructor function
function Hero(name, level) {
this.name = name;
this.level = level;
}
Wenn wir dies in die unten gezeigteclass-Syntax übersetzen, sehen wir, dass sie sehr ähnlich aufgebaut ist.
class.js
// Initializing a class definition
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
}
Wir wissen, dass eine Konstruktorfunktion ein Objektentwurf sein soll, indem der erste Buchstabe des Initialisierers (optional) groß geschrieben wird und die Syntax bekannt ist. Das Schlüsselwortclass
kommuniziert das Ziel unserer Funktion auf einfachere Weise.
Der einzige Unterschied in der Syntax der Initialisierung besteht darin, das Schlüsselwortclass
anstelle vonfunction
zu verwenden und die Eigenschaften innerhalb einerconstructor()
-Methode zuzuweisen.
Methoden definieren
Die übliche Praxis bei Konstruktorfunktionen besteht darin, Methoden direkt denprototype
zuzuweisen, anstatt in der Initialisierung, wie in der folgenden Methodegreet()
gezeigt.
constructor.js
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. Mit den in ES6 eingeführtenmethod definition shorthand ist das Definieren einer Methode ein noch präziserer Prozess.
class.js
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 werden eine neue Instanz vonHero
mit dem Schlüsselwortnew
erstellen und einige Werte zuweisen.
const hero1 = new Hero('Varg', 1);
Wenn wir mitconsole.log(hero1)
weitere Informationen zu unserem neuen Objekt ausdrucken, sehen wir weitere Details darüber, was mit der Klasseninitialisierung geschieht.
OutputHero {name: "Varg", level: 1}
__proto__:
▶ constructor: class Hero
▶ greet: ƒ greet()
Wir können in der Ausgabe sehen, dass die Funktionenconstructor()
undgreet()
auf__proto__
oder[[Prototype]]
vonhero1
angewendet wurden und nicht direkt als Methode für dashero1
Objekt. 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, opfern dabei jedoch etwas Klarheit.
Klasse erweitern
Konstruktorfunktionen und -klassen zeichnen sich dadurch aus, 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 mit der Methodecall()
vom übergeordneten Element erstellt werden. Im folgenden Beispiel erstellen wir eine spezifischere Zeichenklasse mit dem NamenMage
und weisen ihr die Eigenschaften vonHero
mithilfe voncall()
zu. Außerdem fügen wir eine zusätzliche Eigenschaft hinzu.
constructor.js
// 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 vonMage
mit denselben Eigenschaften wieHero
sowie eine neue Instanz erstellen, die wir hinzugefügt haben.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Wenn Siehero2
an die Konsole senden, können Sie sehen, dass wir basierend auf dem Konstruktor ein neuesMage
erstellt haben.
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__:
▶ constructor: ƒ Mage(name, level, spell)
Bei ES6-Klassen wird anstelle voncall
das Schlüsselwortsuper
verwendet, um auf die übergeordneten Funktionen zuzugreifen. Wir werdenextends
verwenden, um auf die übergeordnete Klasse zu verweisen.
class.js
// 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 auf die gleiche Weise eine neueMage
-Instanz erstellen.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Wir werdenhero2
auf die Konsole drucken und die Ausgabe anzeigen.
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 FallHero
.
Im Folgenden finden Sie einen direkten Vergleich des gesamten Prozesses der Initialisierung, des Hinzufügens von Methoden und der Vererbung einer Konstruktorfunktion und einer Klasse.
constructor.js
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;
}
class.js
// 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 ist, ist das zugrunde liegende Ergebnis zwischen beiden Methoden nahezu gleich. Klassen ermöglichen eine präzisere Erstellung von Objektentwürfen, und Konstruktorfunktionen beschreiben genauer, was unter der Haube geschieht.
Fazit
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, um ein effektiver JavaScript-Entwickler zu sein. Das Kennenlernen von Klassen ist äußerst hilfreich, da beliebte JavaScript-Bibliotheken wieReacthäufig die Syntax vonclass
verwenden.