Klassenvererbung ist eine Möglichkeit für eine Klasse, eine andere Klasse zu erweitern.
So können wir neue Funktionen zusätzlich zu den vorhandenen erstellen.
Nehmen wir an, wir haben die Klasse Animal
:
Klasse Tier { Konstruktor(Name) { this.speed = 0; this.name = Name; } laufen(Geschwindigkeit) { this.speed = speed; Alert(`${this.name} läuft mit der Geschwindigkeit ${this.speed}.`); } stoppen() { this.speed = 0; alarm(`${this.name} steht still.`); } } let animal = new Animal("Mein Tier");
So können wir animal
und Animal
grafisch darstellen:
…Und wir möchten eine weitere class Rabbit
erstellen.
Da Kaninchen Tiere sind, sollte Rabbit
-Klasse auf Animal
basieren und Zugang zu Tiermethoden haben, damit Kaninchen das tun können, was „generische“ Tiere können.
Die Syntax zum Erweitern einer anderen Klasse lautet: class Child extends Parent
.
Erstellen wir class Rabbit
die von Animal
erbt:
Klasse Rabbit erweitert Animal { verstecken() { alarm(`${this.name} versteckt sich!`); } } let Rabbit = new Rabbit("White Rabbit"); Rabbit.run(5); // White Rabbit rennt mit Geschwindigkeit 5. Rabbit.hide(); // Weißes Kaninchen versteckt sich!
Objekte der Rabbit
-Klasse haben Zugriff sowohl auf Rabbit
-Methoden wie rabbit.hide()
als auch auf Animal
-Methoden wie rabbit.run()
.
Intern funktioniert das Schlüsselwort extends
mit der guten alten Prototyp-Mechanik. Es setzt Rabbit.prototype.[[Prototype]]
auf Animal.prototype
. Wenn also eine Methode in Rabbit.prototype
nicht gefunden wird, übernimmt JavaScript sie aus Animal.prototype
.
Um beispielsweise die rabbit.run
-Methode zu finden, prüft die Engine (unten oben im Bild):
Das rabbit
Objekt (hat kein run
).
Sein Prototyp ist Rabbit.prototype
(hat hide
, aber nicht run
).
Sein Prototyp, also (aufgrund von extends
) Animal.prototype
, hat schließlich die run
-Methode.
Wie wir uns aus dem Kapitel „Native Prototypen“ erinnern können, verwendet JavaScript selbst prototypische Vererbung für integrierte Objekte. Beispielsweise ist Date.prototype.[[Prototype]]
Object.prototype
. Aus diesem Grund haben Datumsangaben Zugriff auf generische Objektmethoden.
Nach extends
ist jeder Ausdruck zulässig
Die Klassensyntax ermöglicht die Angabe nicht nur einer Klasse, sondern eines beliebigen Ausdrucks nach extends
.
Zum Beispiel ein Funktionsaufruf, der die übergeordnete Klasse generiert:
Funktion f(Phrase) { Rückgabeklasse { sayHi() { alarm(phrase); } }; } Klasse Benutzer erweitert f("Hallo") {} neuer Benutzer().sayHi(); // Hallo
Hier erbt class User
vom Ergebnis von f("Hello")
.
Dies kann für fortgeschrittene Programmiermuster nützlich sein, wenn wir Funktionen verwenden, um Klassen abhängig von vielen Bedingungen zu generieren und von ihnen erben zu können.
Gehen wir nun weiter und überschreiben eine Methode. Standardmäßig werden alle Methoden, die nicht in class Rabbit
angegeben sind, direkt „wie sie sind“ aus class Animal
übernommen.
Wenn wir jedoch in Rabbit
unsere eigene Methode angeben, z. B. stop()
wird diese stattdessen verwendet:
Klasse Rabbit erweitert Animal { stoppen() { // ...jetzt wird dies für Rabbit.stop() verwendet // anstelle von stop() aus der Klasse Animal } }
Normalerweise möchten wir eine übergeordnete Methode jedoch nicht vollständig ersetzen, sondern darauf aufbauen, um ihre Funktionalität zu optimieren oder zu erweitern. Wir machen etwas in unserer Methode, rufen aber die übergeordnete Methode vorher/nachher oder im Prozess auf.
Klassen stellen hierfür das Schlüsselwort "super"
bereit.
super.method(...)
um eine übergeordnete Methode aufzurufen.
super(...)
um einen übergeordneten Konstruktor aufzurufen (nur innerhalb unseres Konstruktors).
Lassen Sie zum Beispiel unser Kaninchen automatisch ausblenden, wenn es angehalten wird:
Klasse Tier { Konstruktor(Name) { this.speed = 0; this.name = Name; } laufen(Geschwindigkeit) { this.speed = speed; Alert(`${this.name} läuft mit der Geschwindigkeit ${this.speed}.`); } stoppen() { this.speed = 0; alarm(`${this.name} steht still.`); } } Klasse Rabbit erweitert Animal { verstecken() { alarm(`${this.name} versteckt sich!`); } stoppen() { super.stop(); // übergeordneten Stopp aufrufen this.hide(); // und dann verstecken } } let Rabbit = new Rabbit("White Rabbit"); Rabbit.run(5); // White Rabbit rennt mit Geschwindigkeit 5. Rabbit.stop(); // White Rabbit steht still. White Rabbit versteckt sich!
Jetzt verfügt Rabbit
über die stop
-Methode, die im Prozess das übergeordnete Element super.stop()
aufruft.
Pfeilfunktionen haben kein super
Wie im Kapitel „Pfeilfunktionen überarbeitet“ erwähnt wurde, verfügen Pfeilfunktionen nicht über super
.
Wenn darauf zugegriffen wird, wird es von der äußeren Funktion übernommen. Zum Beispiel:
Klasse Rabbit erweitert Animal { stoppen() { setTimeout(() => super.stop(), 1000); // Aufruf des übergeordneten Elements stoppt nach 1 Sekunde } }
Der super
in der Pfeilfunktion ist derselbe wie in stop()
, funktioniert also wie vorgesehen. Wenn wir hier eine „reguläre“ Funktion angeben würden, gäbe es einen Fehler:
// Unerwartetes Super setTimeout(function() { super.stop() }, 1000);
Bei Konstruktoren wird es etwas knifflig.
Bisher hatte Rabbit
keinen eigenen constructor
.
Wenn eine Klasse eine andere Klasse erweitert und keinen constructor
hat, wird laut Spezifikation der folgende „leere“ constructor
generiert:
Klasse Rabbit erweitert Animal { // generiert zur Erweiterung von Klassen ohne eigene Konstruktoren Konstruktor(...Argumente) { super(...Argumente); } }
Wie wir sehen können, ruft es im Grunde den übergeordneten constructor
auf und übergibt ihm alle Argumente. Das passiert, wenn wir keinen eigenen Konstruktor schreiben.
Fügen wir nun einen benutzerdefinierten Konstruktor zu Rabbit
hinzu. Zusätzlich zum name
wird die earLength
angegeben:
Klasse Tier { Konstruktor(Name) { this.speed = 0; this.name = Name; } // ... } Klasse Rabbit erweitert Animal { Konstruktor(Name, Ohrlänge) { this.speed = 0; this.name = Name; this.earLength = earLength; } // ... } // Funktioniert nicht! let Rabbit = new Rabbit("White Rabbit", 10); // Fehler: Dies ist nicht definiert.
Hoppla! Wir haben einen Fehler. Jetzt können wir keine Kaninchen erschaffen. Was ist schief gelaufen?
Die kurze Antwort lautet:
Konstruktoren in erbenden Klassen müssen super(...)
aufrufen und (!) dies tun, bevor sie this
verwenden.
…Aber warum? Was ist hier los? Tatsächlich erscheint die Anforderung seltsam.
Natürlich gibt es eine Erklärung. Lassen Sie uns ins Detail gehen, damit Sie wirklich verstehen, worum es geht.
In JavaScript wird zwischen einer Konstruktorfunktion einer erbenden Klasse (sogenannter „abgeleiteter Konstruktor“) und anderen Funktionen unterschieden. Ein abgeleiteter Konstruktor verfügt über eine spezielle interne Eigenschaft [[ConstructorKind]]:"derived"
. Das ist ein spezielles internes Label.
Dieses Label beeinflusst sein Verhalten bei new
.
Wenn eine reguläre Funktion mit new
ausgeführt wird, erstellt sie ein leeres Objekt und weist es this
zu.
Wenn jedoch ein abgeleiteter Konstruktor ausgeführt wird, geschieht dies nicht. Es wird erwartet, dass der übergeordnete Konstruktor diese Aufgabe übernimmt.
Daher muss ein abgeleiteter Konstruktor super
aufrufen, um seinen übergeordneten (Basis-)Konstruktor auszuführen, andernfalls wird das Objekt this
nicht erstellt. Und wir erhalten eine Fehlermeldung.
Damit der Rabbit
-Konstruktor funktioniert, muss er super()
aufrufen, bevor er this
verwendet, wie hier:
Klasse Tier { Konstruktor(Name) { this.speed = 0; this.name = Name; } // ... } Klasse Rabbit erweitert Animal { Konstruktor(Name, Ohrlänge) { super(Name); this.earLength = earLength; } // ... } // jetzt gut let Rabbit = new Rabbit("White Rabbit", 10); alarm(rabbit.name); // Weißes Kaninchen alarm(rabbit.earLength); // 10
Erweiterter Hinweis
Dieser Hinweis setzt voraus, dass Sie über gewisse Erfahrungen mit Kursen verfügen, möglicherweise in anderen Programmiersprachen.
Es bietet einen besseren Einblick in die Sprache und erklärt auch das Verhalten, das eine Fehlerquelle sein könnte (aber nicht sehr oft).
Wenn es Ihnen schwerfällt, es zu verstehen, lesen Sie einfach weiter und kehren Sie einige Zeit später noch einmal zurück.
Wir können nicht nur Methoden, sondern auch Klassenfelder überschreiben.
Allerdings gibt es ein kniffliges Verhalten, wenn wir im übergeordneten Konstruktor auf ein überschriebenes Feld zugreifen, ganz anders als bei den meisten anderen Programmiersprachen.
Betrachten Sie dieses Beispiel:
Klasse Tier { name = 'Tier'; Konstruktor() { alarm(this.name); // (*) } } Klasse Rabbit erweitert Animal { name = 'Kaninchen'; } neues Tier(); // Tier neues Kaninchen(); // Tier
Hier erweitert die Klasse Rabbit
Animal
und überschreibt das name
mit seinem eigenen Wert.
Da es in Rabbit
keinen eigenen Konstruktor gibt, wird der Animal
-Konstruktor aufgerufen.
Interessant ist, dass in beiden Fällen: new Animal()
und new Rabbit()
die alert
in der Zeile (*)
animal
anzeigt.
Mit anderen Worten: Der übergeordnete Konstruktor verwendet immer seinen eigenen Feldwert, nicht den überschriebenen.
Was ist daran seltsam?
Wenn es noch nicht klar ist, vergleichen Sie bitte mit den Methoden.
Hier ist der gleiche Code, aber anstelle des Feldes this.name
rufen wir die Methode this.showName()
auf:
Klasse Tier { showName() { // anstelle von this.name = 'animal' alarm('animal'); } Konstruktor() { this.showName(); // anstelle von Alert(this.name); } } Klasse Rabbit erweitert Animal { showName() { alarm('rabbit'); } } neues Tier(); // Tier neues Kaninchen(); // Kaninchen
Bitte beachten Sie: Jetzt ist die Ausgabe anders.
Und das erwarten wir natürlich auch. Wenn der übergeordnete Konstruktor in der abgeleiteten Klasse aufgerufen wird, verwendet er die überschriebene Methode.
…Bei Klassenfeldern ist dies jedoch nicht der Fall. Wie gesagt, der übergeordnete Konstruktor verwendet immer das übergeordnete Feld.
Warum gibt es einen Unterschied?
Der Grund liegt in der Reihenfolge der Feldinitialisierung. Das Klassenfeld wird initialisiert:
Vor dem Konstruktor für die Basisklasse (der nichts erweitert),
Unmittelbar nach super()
für die abgeleitete Klasse.
In unserem Fall ist Rabbit
die abgeleitete Klasse. Es gibt keinen constructor()
darin. Wie bereits erwähnt, ist das dasselbe, als ob es einen leeren Konstruktor mit nur super(...args)
gäbe.
Also ruft new Rabbit()
super()
auf und führt so den übergeordneten Konstruktor aus, und (gemäß der Regel für abgeleitete Klassen) werden erst danach seine Klassenfelder initialisiert. Zum Zeitpunkt der Ausführung des übergeordneten Konstruktors gibt es noch keine Rabbit
Klassenfelder, deshalb werden Animal
-Felder verwendet.
Dieser subtile Unterschied zwischen Feldern und Methoden ist spezifisch für JavaScript.
Glücklicherweise zeigt sich dieses Verhalten nur, wenn im übergeordneten Konstruktor ein überschriebenes Feld verwendet wird. Dann kann es schwierig sein zu verstehen, was los ist, deshalb erklären wir es hier.
Wenn es zu einem Problem wird, kann man es beheben, indem man statt Feldern Methoden oder Getter/Setter verwendet.
Erweiterte Informationen
Wenn Sie das Tutorial zum ersten Mal lesen, kann dieser Abschnitt übersprungen werden.
Es geht um die internen Mechanismen hinter Vererbung und super
.
Lassen Sie uns etwas tiefer unter die Haube von super
gehen. Unterwegs werden wir einige interessante Dinge sehen.
Zunächst einmal: Nach allem, was wir bisher gelernt haben, ist es unmöglich, dass super
überhaupt funktioniert!
Ja, in der Tat, fragen wir uns: Wie soll das technisch funktionieren? Wenn eine Objektmethode ausgeführt wird, ruft sie das aktuelle Objekt als this
ab. Wenn wir dann super.method()
aufrufen, muss die Engine die method
vom Prototyp des aktuellen Objekts abrufen. Aber wie?
Die Aufgabe mag einfach erscheinen, ist es aber nicht. Die Engine kennt das aktuelle Objekt this
und könnte daher die übergeordnete method
als this.__proto__.method
erhalten. Leider wird eine solche „naive“ Lösung nicht funktionieren.
Lassen Sie uns das Problem demonstrieren. Ohne Klassen, der Einfachheit halber werden einfache Objekte verwendet.
Sie können diesen Teil überspringen und weiter unten zum Unterabschnitt [[HomeObject]]
gehen, wenn Sie die Details nicht wissen möchten. Das wird nicht schaden. Oder lesen Sie weiter, wenn Sie daran interessiert sind, die Dinge im Detail zu verstehen.
Im folgenden Beispiel rabbit.__proto__ = animal
. Versuchen wir es nun: In rabbit.eat()
rufen wir animal.eat()
auf und verwenden dabei this.__proto__
:
let animal = { Name: „Tier“, essen() { alarm(`${this.name} isst.`); } }; lass Kaninchen = { __proto__: Tier, Name: „Kaninchen“, essen() { // so könnte super.eat() vermutlich funktionieren this.__proto__.eat.call(this); // (*) } }; Rabbit.eat(); // Kaninchen frisst.
In der Zeile (*)
nehmen wir eat
vom Prototyp ( animal
) und rufen ihn im Kontext des aktuellen Objekts auf. Bitte beachten Sie, dass .call(this)
hier wichtig ist, da ein einfaches this.__proto__.eat()
das übergeordnete eat
im Kontext des Prototyps und nicht im aktuellen Objekt ausführen würde.
Und im obigen Code funktioniert es tatsächlich wie beabsichtigt: Wir haben die richtige alert
.
Nun fügen wir der Kette ein weiteres Objekt hinzu. Wir werden sehen, wie die Dinge kaputt gehen:
let animal = { Name: „Tier“, essen() { alarm(`${this.name} isst.`); } }; lass Kaninchen = { __proto__: Tier, essen() { // ...hüpfe im Kaninchenstil herum und rufe die übergeordnete (Tier-)Methode auf this.__proto__.eat.call(this); // (*) } }; sei longEar = { __proto__: Kaninchen, essen() { // ...etwas mit langen Ohren machen und Elternmethode (Kaninchen) aufrufen this.__proto__.eat.call(this); // (**) } }; longEar.eat(); // Fehler: Maximale Aufrufstapelgröße überschritten
Der Code funktioniert nicht mehr! Wir können den Fehler beim Aufrufen von longEar.eat()
sehen.
Es ist vielleicht nicht so offensichtlich, aber wenn wir den Aufruf longEar.eat()
verfolgen, können wir sehen, warum. In beiden Zeilen (*)
und (**)
ist der Wert this
das aktuelle Objekt ( longEar
). Das ist wichtig: Alle Objektmethoden erhalten das aktuelle Objekt als this
, nicht als Prototyp oder so.
In beiden Zeilen (*)
und (**)
ist der Wert von this.__proto__
also genau derselbe: rabbit
. Beide rufen rabbit.eat
auf, ohne in der Endlosschleife die Kette hinaufzugehen.
Hier ist das Bild von dem, was passiert:
Innerhalb von longEar.eat()
ruft die Zeile (**)
rabbit.eat
auf und versorgt es mit this=longEar
.
// in longEar.eat() haben wir this = longEar this.__proto__.eat.call(this) // (**) // wird longEar.__proto__.eat.call(this) // das ist Rabbit.eat.call(this);
Dann würden wir in der Zeile (*)
von rabbit.eat
den Aufruf gerne noch weiter oben in der Kette weiterleiten, aber this=longEar
, also ist this.__proto__.eat
wieder rabbit.eat
!
// in Rabbit.eat() haben wir auch this = longEar this.__proto__.eat.call(this) // (*) // wird longEar.__proto__.eat.call(this) // oder (wieder) Rabbit.eat.call(this);
…So ruft sich rabbit.eat
in der Endlosschleife auf, weil es nicht weiter aufsteigen kann.
Das Problem lässt sich this
allein nicht lösen.
[[HomeObject]]
Um die Lösung bereitzustellen, fügt JavaScript eine weitere spezielle interne Eigenschaft für Funktionen hinzu: [[HomeObject]]
.
Wenn eine Funktion als Klassen- oder Objektmethode angegeben wird, wird ihre [[HomeObject]]
Eigenschaft zu diesem Objekt.
Dann verwendet super
es, um den übergeordneten Prototyp und seine Methoden aufzulösen.
Sehen wir uns an, wie es funktioniert, zunächst mit einfachen Objekten:
let animal = { Name: „Tier“, eat() { // animal.eat.[[HomeObject]] == animal alarm(`${this.name} isst.`); } }; lass Kaninchen = { __proto__: Tier, Name: „Kaninchen“, eat() { // Rabbit.eat.[[HomeObject]] == Rabbit super.eat(); } }; sei longEar = { __proto__: Kaninchen, Name: „Langohr“, eat() { // longEar.eat.[[HomeObject]] == longEar super.eat(); } }; // funktioniert korrekt longEar.eat(); // Langohr isst.
Aufgrund der [[HomeObject]]
Mechanik funktioniert es wie vorgesehen. Eine Methode wie longEar.eat
kennt ihr [[HomeObject]]
und übernimmt die übergeordnete Methode von ihrem Prototyp. Ohne jegliche Verwendung this
.
Wie wir bereits wissen, sind Funktionen im Allgemeinen „kostenlos“ und nicht an Objekte in JavaScript gebunden. Sie können also zwischen Objekten kopiert und mit einem anderen aufgerufen werden this
.
Die bloße Existenz von [[HomeObject]]
verstößt gegen dieses Prinzip, da sich Methoden an ihre Objekte erinnern. [[HomeObject]]
kann nicht geändert werden, daher ist diese Bindung für immer.
Die einzige Stelle in der Sprache, an der [[HomeObject]]
verwendet wird, ist super
. Wenn eine Methode also nicht super
verwendet, können wir sie dennoch als frei betrachten und zwischen Objekten kopieren. Aber mit super
kann etwas schief gehen.
Hier ist die Demo eines falschen super
Ergebnisses nach dem Kopieren:
let animal = { sayHi() { alarm(`Ich bin ein Tier`); } }; // Kaninchen erbt vom Tier lass Kaninchen = { __proto__: Tier, sayHi() { super.sayHi(); } }; lass pflanzen = { sayHi() { alarm("Ich bin eine Pflanze"); } }; // Baum erbt von Pflanze let tree = { __proto__: Pflanze, sayHi: Rabbit.sayHi // (*) }; tree.sayHi(); // Ich bin ein Tier (?!?)
Ein Aufruf von tree.sayHi()
zeigt „Ich bin ein Tier“. Auf jeden Fall falsch.
Der Grund ist einfach:
In der Zeile (*)
wurde die Methode tree.sayHi
von rabbit
kopiert. Vielleicht wollten wir einfach nur Codeduplizierungen vermeiden?
Sein [[HomeObject]]
ist rabbit
, da es in rabbit
erstellt wurde. Es gibt keine Möglichkeit, [[HomeObject]]
zu ändern.
Der Code von tree.sayHi()
enthält super.sayHi()
. Es geht von rabbit
auf und übernimmt die Methode von animal
.
Hier ist das Diagramm, was passiert:
[[HomeObject]]
ist für Methoden sowohl in Klassen als auch in einfachen Objekten definiert. Für Objekte müssen Methoden jedoch genau als method()
angegeben werden, nicht als "method: function()"
.
Der Unterschied mag für uns unwesentlich sein, für JavaScript ist er jedoch wichtig.
Im folgenden Beispiel wird zum Vergleich eine Nicht-Methoden-Syntax verwendet. Die Eigenschaft [[HomeObject]]
ist nicht festgelegt und die Vererbung funktioniert nicht:
let animal = { eat: function() { // absichtlich so schreiben, statt eat() {... // ... } }; lass Kaninchen = { __proto__: Tier, eat: function() { super.eat(); } }; Rabbit.eat(); // Fehler beim Aufruf von super (da es kein [[HomeObject]] gibt)
So erweitern Sie eine Klasse: class Child extends Parent
:
Das bedeutet, Child.prototype.__proto__
Parent.prototype
sein wird, sodass Methoden vererbt werden.
Beim Überschreiben eines Konstruktors:
Wir müssen den übergeordneten Konstruktor als super()
im Child
Konstruktor aufrufen, bevor wir this
verwenden.
Beim Überschreiben einer anderen Methode:
Wir können super.method()
in einer Child
-Methode verwenden, um Parent
-Methode aufzurufen.
Interna:
Methoden merken sich ihre Klasse/ihr Objekt in der internen Eigenschaft [[HomeObject]]
. So löst super
übergeordnete Methoden auf.
Daher ist es nicht sicher, eine Methode mit super
von einem Objekt auf ein anderes zu kopieren.
Auch:
Pfeilfunktionen haben kein eigenes this
oder super
, sodass sie sich transparent in den umgebenden Kontext einfügen.
Wichtigkeit: 5
Hier ist der Code mit Rabbit
Animal
erweitert.
Leider können keine Rabbit
-Objekte erstellt werden. Was ist los? Repariere es.
Klasse Tier { Konstruktor(Name) { this.name = Name; } } Klasse Rabbit erweitert Animal { Konstruktor(Name) { this.name = Name; this.created = Date.now(); } } let Rabbit = new Rabbit("White Rabbit"); // Fehler: Dies ist nicht definiert alarm(rabbit.name);
Das liegt daran, dass der untergeordnete Konstruktor super()
aufrufen muss.
Hier ist der korrigierte Code:
Klasse Tier { Konstruktor(Name) { this.name = Name; } } Klasse Rabbit erweitert Animal { Konstruktor(Name) { super(Name); this.created = Date.now(); } } let Rabbit = new Rabbit("White Rabbit"); // ok jetzt alarm(rabbit.name); // Weißes Kaninchen
Wichtigkeit: 5
Wir haben eine Clock
. Ab sofort wird die Uhrzeit jede Sekunde gedruckt.
Klasse Uhr { Konstruktor({ template }) { this.template = Vorlage; } render() { let date = new Date(); let hours = date.getHours(); if (Stunden < 10) Stunden = '0' + Stunden; let mins = date.getMinutes(); if (Minuten < 10) Minuten = '0' + Minuten; let secs = date.getSeconds(); if (Sek. < 10) Sek. = '0' + Sek.; let Output = this.template .replace('h', Stunden) .replace('m', Min.) .replace('s', secs); console.log(Ausgabe); } stoppen() { clearInterval(this.timer); } Start() { this.render(); this.timer = setInterval(() => this.render(), 1000); } }
Erstellen Sie eine neue Klasse ExtendedClock
, die von Clock
erbt und den Parameter precision
– die Anzahl der ms
zwischen „Ticks“ – hinzufügt. Sollte standardmäßig 1000
(1 Sekunde) sein.
Ihr Code sollte sich in der Datei extended-clock.js
befinden
Ändern Sie nicht die ursprüngliche clock.js
. Verlängern Sie es.
Öffnen Sie eine Sandbox für die Aufgabe.
Klasse ExtendedClock erweitert Clock { Konstruktor(Optionen) { super(Optionen); let { precision = 1000 } = options; this.precision = Präzision; } Start() { this.render(); this.timer = setInterval(() => this.render(), this.precision); } };
Öffnen Sie die Lösung in einer Sandbox.