One place for hosting & domains

      Klassen

      Grundlegendes zu Klassen in JavaScript


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

      Klassen sind Funktionen

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

      Output

      x {} constructor: ƒ ()

      Dies gilt auch für Klassen.

      const y = class {}
      
      // Initialize a constructor from a class
      const constructorFromClass = new y();
      
      console.log(constructorFromClass);
      

      Output

      y {} constructor: class

      Diese Prototyp-Konstruktorbeispiele sind ansonsten leer, aber wir können sehen, wie unter der Syntax beide Methoden das gleiche Endergebnis erzielen.

      Definieren einer Klasse

      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.

      constructor.js

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

      class.js

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

      Definieren von Methoden

      Die übliche Vorgehensweise bei Konstruktorfunktionen besteht darin, Methoden anstatt in der Initialisierung direkt dem prototype zuzuweisen, wie in der greet ()-Methode unten dargestellt.

      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. Unter Verwendung der in ES6 eingeführten Kurzform method definition 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 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.

      Output

      Hero {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.

      Erweitern einer Klasse

      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.

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

      Output

      Mage {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.

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

      Output

      Mage {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.

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

      Zusammenfassung

      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.



      Source link