Wir können auch der Klasse als Ganzes eine Methode zuweisen. Solche Methoden werden als statisch bezeichnet.
In einer Klassendeklaration wird ihnen das Schlüsselwort static
vorangestellt, etwa so:
Klasse Benutzer { static staticMethod() { alarm(this === Benutzer); } } User.staticMethod(); // WAHR
Das hat eigentlich das Gleiche, als würde man es direkt als Eigenschaft zuweisen:
Klassenbenutzer { } User.staticMethod = function() { alarm(this === Benutzer); }; User.staticMethod(); // WAHR
Der Wert this
Aufrufs von User.staticMethod()
ist der Klassenkonstruktor User
selbst (die Regel „Objekt vor Punkt“).
Normalerweise werden statische Methoden verwendet, um Funktionen zu implementieren, die zur Klasse als Ganzes, aber nicht zu einem bestimmten Objekt davon gehören.
Wir haben zum Beispiel Article
-Objekte und benötigen eine Funktion, um sie zu vergleichen.
Eine natürliche Lösung wäre das Hinzufügen der statischen Methode Article.compare
:
Klasse Artikel { Konstruktor(Titel, Datum) { this.title = Titel; this.date = date; } statischer Vergleich (ArtikelA, ArtikelB) { return ArticleA.date - ArticleB.date; } } // Nutzung let Articles = [ neuer Artikel("HTML", neues Datum(2019, 1, 1)), neuer Artikel("CSS", neues Datum(2019, 0, 1)), neuer Artikel("JavaScript", neues Datum(2019, 11, 1)) ]; Articles.sort(Article.compare); Alert( Articles[0].title ); // CSS
Hier steht die Article.compare
-Methode „über“ Artikeln, um sie zu vergleichen. Es handelt sich nicht um eine Methode eines Artikels, sondern um eine Methode der gesamten Klasse.
Ein weiteres Beispiel wäre eine sogenannte „Factory“-Methode.
Nehmen wir an, wir benötigen mehrere Möglichkeiten, einen Artikel zu erstellen:
Erstellen Sie anhand vorgegebener Parameter ( title
, date
usw.).
Erstellen Sie einen leeren Artikel mit dem heutigen Datum.
…oder sonst irgendwie.
Der erste Weg kann vom Konstruktor implementiert werden. Und für die zweite Möglichkeit können wir eine statische Methode der Klasse erstellen.
Wie Article.createTodays()
hier:
Klasse Artikel { Konstruktor(Titel, Datum) { this.title = Titel; this.date = Datum; } static createTodays() { // Denken Sie daran, dies = Artikel return new this("Heutiger Digest", new Date()); } } let Article = Article.createTodays(); Alert( Artikel.Titel ); // Die heutige Übersicht
Jedes Mal, wenn wir einen heutigen Digest erstellen müssen, können wir Article.createTodays()
aufrufen. Auch hier handelt es sich nicht um eine Methode eines Artikels, sondern um eine Methode der gesamten Klasse.
Statische Methoden werden auch in datenbankbezogenen Klassen verwendet, um Einträge in der Datenbank zu suchen/speichern/entfernen, wie folgt:
// vorausgesetzt, dass Article eine spezielle Klasse zum Verwalten von Artikeln ist // statische Methode zum Entfernen des Artikels nach ID: Article.remove({id: 12345});
Statische Methoden sind für einzelne Objekte nicht verfügbar
Statische Methoden können für Klassen aufgerufen werden, nicht für einzelne Objekte.
Beispielsweise funktioniert dieser Code nicht:
// ... Article.createTodays(); /// Fehler: Article.createTodays ist keine Funktion
Eine neue Ergänzung
Dies ist eine neue Ergänzung der Sprache. Beispiele funktionieren im aktuellen Chrome.
Statische Eigenschaften sind ebenfalls möglich. Sie sehen aus wie reguläre Klasseneigenschaften, werden jedoch durch static
vorangestellt:
Klasse Artikel { static editor = „Ilya Kantor“; } Alert( Article.publisher ); // Ilja Kantor
Das ist dasselbe wie eine direkte Zuweisung zu Article
:
Article.publisher = „Ilya Kantor“;
Statische Eigenschaften und Methoden werden vererbt.
Beispielsweise werden Animal.compare
und Animal.planet
im folgenden Code geerbt und sind als Rabbit.compare
und Rabbit.planet
zugänglich:
Klasse Tier { static planet = „Erde“; Konstruktor(Name, Geschwindigkeit) { this.speed = speed; this.name = Name; } laufen(Geschwindigkeit = 0) { this.speed += speed; Alert(`${this.name} läuft mit der Geschwindigkeit ${this.speed}.`); } statisch vergleichen(TierA, TierB) { return animalA.speed - animalB.speed; } } // Von Animal erben Klasse Rabbit erweitert Animal { verstecken() { alarm(`${this.name} versteckt sich!`); } } lass Kaninchen = [ neues Kaninchen("White Rabbit", 10), neues Kaninchen("Schwarzes Kaninchen", 5) ]; Rabbits.sort(Rabbit.compare); Kaninchen[0].run(); // Black Rabbit läuft mit Geschwindigkeit 5. alarm(Rabbit.planet); // Erde
Wenn wir nun Rabbit.compare
aufrufen, wird das geerbte Animal.compare
aufgerufen.
Wie funktioniert es? Auch hier werden Prototypen verwendet. Wie Sie vielleicht schon vermutet haben, gibt extends
Rabbit
den [[Prototype]]
Verweis auf Animal
.
Rabbit extends Animal
erstellt zwei [[Prototype]]
-Referenzen:
Die Rabbit
erbt prototypisch von der Animal
.
Rabbit.prototype
erbt prototypisch von Animal.prototype
.
Daher funktioniert die Vererbung sowohl für reguläre als auch für statische Methoden.
Lassen Sie uns das hier anhand des Codes überprüfen:
Klasse Tier {} Klasse Rabbit erweitert Animal {} // für Statik Alert(Rabbit.__proto__ === Animal); // WAHR // für reguläre Methoden Alert(Rabbit.prototype.__proto__ === Animal.prototype); // WAHR
Statische Methoden werden für die Funktionalität verwendet, die zur Klasse „als Ganzes“ gehört. Es bezieht sich nicht auf eine konkrete Klasseninstanz.
Zum Beispiel eine Vergleichsmethode Article.compare(article1, article2)
oder eine Factory-Methode Article.createTodays()
.
Sie werden in der Klassendeklaration mit dem Wort static
gekennzeichnet.
Statische Eigenschaften werden verwendet, wenn wir Daten auf Klassenebene speichern möchten, auch nicht an eine Instanz gebunden.
Die Syntax lautet:
Klasse MyClass { statische Eigenschaft = ...; statische Methode() { ... } }
Technisch gesehen ist die statische Deklaration dasselbe wie die Zuweisung an die Klasse selbst:
MyClass.property = ... MyClass.method = ...
Statische Eigenschaften und Methoden werden vererbt.
Für class B extends A
der Prototyp der Klasse B
selbst zeigt auf A
: B.[[Prototype]] = A
. Wenn also ein Feld in B
nicht gefunden wird, wird die Suche in A
fortgesetzt.
Wichtigkeit: 3
Wie wir wissen, erben alle Objekte normalerweise von Object.prototype
und erhalten Zugriff auf „generische“ Objektmethoden wie hasOwnProperty
usw.
Zum Beispiel:
Klasse Kaninchen { Konstruktor(Name) { this.name = Name; } } let Rabbit = new Rabbit("Rab"); // hasOwnProperty-Methode stammt aus Object.prototype alarm( Rabbit.hasOwnProperty('name') ); // WAHR
Aber wenn wir es explizit so formulieren: "class Rabbit extends Object"
, dann würde sich das Ergebnis von einem einfachen "class Rabbit"
unterscheiden?
Was ist der Unterschied?
Hier ist ein Beispiel für einen solchen Code (er funktioniert nicht – warum? Beheben?):
Klasse Rabbit erweitert Objekt { Konstruktor(Name) { this.name = Name; } } let Rabbit = new Rabbit("Rab"); alarm( Rabbit.hasOwnProperty('name') ); // Fehler
Sehen wir uns zunächst an, warum der letztgenannte Code nicht funktioniert.
Der Grund wird offensichtlich, wenn wir versuchen, es auszuführen. Ein erbender Klassenkonstruktor muss super()
aufrufen. Andernfalls wird "this"
nicht „definiert“.
Also hier ist die Lösung:
Klasse Rabbit erweitert Objekt { Konstruktor(Name) { super(); // Beim Erben muss der übergeordnete Konstruktor aufgerufen werden this.name = Name; } } let Rabbit = new Rabbit("Rab"); alarm( Rabbit.hasOwnProperty('name') ); // WAHR
Aber das ist noch nicht alles.
Auch nach dem Fix gibt es immer noch einen wichtigen Unterschied zwischen "class Rabbit extends Object"
und class Rabbit
.
Wie wir wissen, erstellt die Syntax „extends“ zwei Prototypen:
Zwischen "prototype"
der Konstruktorfunktionen (für Methoden).
Zwischen den Konstruktorfunktionen selbst (für statische Methoden).
Im Fall der class Rabbit extends Object
bedeutet dies:
Klasse Rabbit erweitert Objekt {} alarm( Rabbit.prototype.__proto__ === Object.prototype ); // (1) wahr alarm( Rabbit.__proto__ === Object ); // (2) wahr
Daher bietet Rabbit
jetzt Zugriff auf die statischen Methoden von Object
über Rabbit
, wie folgt:
Klasse Rabbit erweitert Objekt {} // Normalerweise rufen wir Object.getOwnPropertyNames auf Warnung ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b
Aber wenn wir kein extends Object
haben, dann ist Rabbit.__proto__
nicht auf Object
gesetzt.
Hier ist die Demo:
Klasse Kaninchen {} Alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) wahr alarm( Rabbit.__proto__ === Object ); // (2) falsch (!) alarm( Rabbit.__proto__ === Function.prototype ); // standardmäßig als jede Funktion // Fehler, keine solche Funktion in Rabbit Warnung ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Fehler
Daher bietet Rabbit
in diesem Fall keinen Zugriff auf statische Methoden von Object
.
Function.prototype
verfügt übrigens auch über „generische“ Funktionsmethoden, wie call
, bind
usw. Diese stehen letztlich in beiden Fällen zur Verfügung, denn für den eingebauten Object
Konstruktor Object.__proto__ === Function.prototype
.
Hier ist das Bild:
Um es kurz zu machen: Es gibt zwei Unterschiede:
Klasse Kaninchen | Klasse Rabbit erweitert Objekt |
---|---|
– | muss super() im Konstruktor aufrufen |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |