Derzeit verwenden viele von uns in ES5 das folgende Muster in Frameworks, um Klassen und Klassenvariablen zu erstellen, was bequem ist:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
In ES6 können Sie Klassen nativ erstellen, es gibt jedoch keine Option für Klassenvariablen:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Leider funktioniert das oben genannte nicht, da Klassen nur Methoden enthalten können.
Ich verstehe , dass ich kann this.myVar = true
inconstructor
... aber ich möchte meinen Konstruktor nicht "verschmutzen", besonders wenn ich 20-30 + Parameter für eine größere Klasse habe.
Ich habe über viele Möglichkeiten nachgedacht, mit diesem Problem umzugehen, aber noch keine guten gefunden. (Beispiel: Erstellen Sie einen ClassConfig
Handler und übergeben Sie ein parameter
Objekt, das separat von der Klasse deklariert ist. Dann würde der Handler an die Klasse angehängt. Ich habe darüber nachgedacht, WeakMaps
auch irgendwie zu integrieren.)
Welche Ideen hätten Sie, um mit dieser Situation umzugehen?
quelle
this.member = member
bei Ihrem Konstruktor eine Wiederholung mit 20-30 Parametern haben?public variable2 = true
unter Klasse verwenden? Dies würde es auf dem Prototyp definieren.Antworten:
Update 2018:
Es gibt jetzt einen Vorschlag für Stufe 3 - ich freue mich darauf, diese Antwort in einigen Monaten überflüssig zu machen.
In der Zwischenzeit kann jeder, der TypeScript oder babel verwendet, die folgende Syntax verwenden:
Innerhalb eines Klassendeklarations- / Ausdruckskörpers wird eine Variable definiert. Hoffentlich kann ich in ein paar Monaten / Wochen ein Update veröffentlichen.
Update: Chrome 74 wird jetzt mit dieser Syntax ausgeliefert.
Die Notizen im ES-Wiki zum Vorschlag in ES6 ( maximal minimale Klassen ) Hinweis:
Das bedeutet, dass das wonach Sie fragen, berücksichtigt und ausdrücklich abgelehnt wurde.
aber warum?
Gute Frage. Die guten Leute von TC39 möchten, dass Klassendeklarationen die Fähigkeiten einer Klasse deklarieren und definieren. Nicht seine Mitglieder. Eine ES6-Klassendeklaration definiert ihren Vertrag für ihren Benutzer.
Denken Sie daran, dass eine Klassendefinition Prototypmethoden definiert. Das Definieren von Variablen auf dem Prototyp ist im Allgemeinen keine Aufgabe. Sie können natürlich verwenden:
Im Konstruktor, wie Sie vorgeschlagen haben. Siehe auch die Zusammenfassung des Konsenses .
ES7 und darüber hinaus
Es wird an einem neuen Vorschlag für ES7 gearbeitet, der präzisere Instanzvariablen durch Klassendeklarationen und -ausdrücke ermöglicht - https://esdiscuss.org/topic/es7-property-initializers
quelle
static
Eigenschaften erwähnenstatic
das auch nur für Methoden funktioniert.Nur um Benjamins Antwort zu ergänzen: Klassenvariablen sind möglich, aber Sie würden sie nicht verwenden
prototype
, um sie festzulegen.Für eine echte Klassenvariable möchten Sie Folgendes tun:
Innerhalb einer Klassenmethode kann auf diese Variable als
this.constructor.foo
(oderMyClass.foo
) zugegriffen werden .Auf diese Klasseneigenschaften kann normalerweise nicht von der Klasseninstanz aus zugegriffen werden. dh
MyClass.foo
gibt'bar'
abernew MyClass().foo
istundefined
Wenn Sie auch von einer Instanz aus auf Ihre Klassenvariable zugreifen möchten, müssen Sie zusätzlich einen Getter definieren:
Ich habe dies nur mit Traceur getestet, aber ich glaube, dass es in einer Standardimplementierung genauso funktioniert.
JavaScript hat nicht wirklich Klassen . Selbst mit ES6 betrachten wir eher eine objekt- oder prototypbasierte Sprache als eine klassenbasierte Sprache. In jedem
function X () {}
,X.prototype.constructor
zurück PunkteX
. Wenn dernew
Operator verwendet wirdX
, wird ein neues Objekt erstellt, das erbtX.prototype
. Alle undefinierten Eigenschaften in diesem neuen Objekt (einschließlichconstructor
) werden von dort aus nachgeschlagen. Wir können uns dies als Generierung von Objekt- und Klasseneigenschaften vorstellen.quelle
Babel unterstützt Klassenvariablen in ESNext. Überprüfen Sie dieses Beispiel :
quelle
In Ihrem Beispiel:
Da MY_CONST primitiv ist https://developer.mozilla.org/en-US/docs/Glossary/Primitive , können wir einfach Folgendes tun:
Wenn
MY_CONST
es sich jedoch um einen Referenztyp handelt, bei dem diestatic get MY_CONST() {return ['string'];}
Alarmausgabe eine Zeichenfolge ist, ist false . In diesem Falldelete
kann der Bediener den Trick ausführen:Und schließlich für Klassenvariable nicht
const
:quelle
delete
Betreiber, wenn auch aus Leistungsgründen. Was Sie hier eigentlich wollen, istObject.defineProperty
.Da Ihr Problem hauptsächlich stilistisch ist (Sie möchten den Konstruktor nicht mit einer Reihe von Deklarationen füllen), kann es auch stilistisch gelöst werden.
So wie ich es sehe, ist der Konstruktor in vielen klassenbasierten Sprachen eine Funktion, die nach dem Klassennamen selbst benannt ist. Stilistisch könnten wir das verwenden, um eine ES6-Klasse zu erstellen, die stilistisch immer noch sinnvoll ist, aber die typischen Aktionen, die im Konstruktor stattfinden, nicht mit allen Eigenschaftsdeklarationen gruppiert, die wir ausführen. Wir verwenden einfach den eigentlichen JS-Konstruktor als "Deklarationsbereich" und erstellen dann eine Klasse mit dem Namen "Funktion", die wir ansonsten als "Bereich für andere Konstruktoren" behandeln, und rufen sie am Ende des wahren Konstruktors auf.
Beide werden aufgerufen, wenn die neue Instanz erstellt wird.
Sorta mag es, zwei Konstruktoren zu haben, in denen Sie die Deklarationen und die anderen Konstruktoraktionen, die Sie ausführen möchten, trennen, und macht es stilistisch nicht allzu schwer zu verstehen, dass dies auch der Fall ist.
Ich finde, es ist ein guter Stil, wenn man sich mit vielen Deklarationen und / oder vielen Aktionen befasst, die bei der Instanziierung ausgeführt werden müssen, und wenn man die beiden Ideen voneinander unterscheiden möchte.
HINWEIS : Ich verwende sehr absichtlich nicht die typischen Redewendungen des "Initialisierens" (wie eine
init()
oderinitialize()
Methode), da diese oft unterschiedlich verwendet werden. Es gibt eine Art vermuteten Unterschied zwischen der Idee des Konstruierens und Initialisierens. Menschen, die mit Konstruktoren arbeiten, wissen, dass sie im Rahmen der Instanziierung automatisch aufgerufen werden. Wenn Sie eineinit
Methode sehen, gehen viele Leute ohne einen zweiten Blick davon aus, dass sie etwas in der Form von tun müssenvar mc = MyClass(); mc.init();
, denn so initialisieren Sie normalerweise. Ich bin nicht einen Initialisierungsprozess hinzufügen für den Benutzer der Klasse versucht, ich versuche zu addieren , um den Bauprozess der Klasse selbst.Während einige Leute für einen Moment ein Double-Take machen, ist das eigentlich der springende Punkt: Es teilt ihnen mit, dass die Absicht Teil der Konstruktion ist, auch wenn sie dadurch ein bisschen Double-Take-and-Go machen "das ist nicht der Fall Wie ES6-Konstruktoren funktionieren "und nehmen Sie sich einen zweiten Blick auf den eigentlichen Konstruktor, um zu sagen" Oh, sie nennen es unten, wie ich sehe ", das ist weitaus besser, als diese Absicht NICHT zu kommunizieren (oder sie falsch zu kommunizieren) und wahrscheinlich viel davon zu bekommen Leute, die es falsch benutzen und versuchen, es von außen und Junk zu initialisieren. Das ist sehr beabsichtigt für das Muster, das ich vorschlage.
Für diejenigen, die diesem Muster nicht folgen möchten, kann auch das genaue Gegenteil funktionieren. Führen Sie die Deklarationen zu Beginn an eine andere Funktion aus. Nennen Sie es vielleicht "Eigenschaften" oder "publicProperties" oder so. Dann legen Sie den Rest des Materials in den normalen Konstruktor.
Beachten Sie, dass diese zweite Methode möglicherweise sauberer aussieht, aber auch ein inhärentes Problem aufweist, bei dem
properties
eine Klasse, die diese Methode verwendet, eine andere erweitert. Sie müssten eindeutigere Namen angeben, um diesproperties
zu vermeiden. Meine erste Methode hat dieses Problem nicht, da die gefälschte Hälfte des Konstruktors eindeutig nach der Klasse benannt ist.quelle
init
.init
ist ein Muster, das häufig verwendet wird und häufig von außerhalb der Klasse aufgerufen werden soll, wenn der externe Benutzer initialisieren möchte, dvar b = new Thing(); b.init();
. H. Dies ist eine 100% ige Stilwahl, die ich lieber mitteilen möchte, dass es sich um eine zweite Funktion handelt, die automatisch aufgerufen wird, indem die in anderen Sprachen gefundenen Muster ausgenutzt werden. Es ist weitaus weniger wahrscheinlich, dass jemand dies betrachtet und davon ausgeht, dass er dieMyClass
Methode von außen aufrufen muss , und eher, dass er erkennt, dass es sich bei der Absicht um eine zweite Methode handelt, die in der Konstruktion wirkt (dh bei der Instanziierung von selbst aufgerufen wird).MyClass
.$myvar
Standardmethode zum Verweisen auf Variablen, die jQuery-Objekte enthalten sollen, nicht dem Muster ähnelt, $ am Anfang von Variablen zu haben, an das so viele PHP-Programmierer gewöhnt sind. Nur diese kleine Implikation, dass "Ja, es ist nicht genau das Gleiche ... aber sieh mal ... es ist immer noch eine Variable, denn auf diese Weise werden Variablen in einigen Sprachen erstellt!" hilft.properties()
Funktion. Beim Erweitern von Klassen wird es jedoch etwas komplizierter. Wenn Sieproperties()
in allen Klassen eine Funktion zum Deklarieren von Klasseneigenschaften verwenden, wird dem Methodennamen der Klassenname (IE:) vorangestelltMyClassProperties()
, um ein versehentliches Überschreiben zu vermeiden Funktionsaufrufe innerhalb von Unterklassen.super()
Was ist mit dem Oldschool-Weg?
Im Konstruktor erwähnen Sie nur die Variablen, die berechnet werden müssen. Ich mag die Vererbung von Prototypen für diese Funktion - sie kann helfen, viel Speicherplatz zu sparen (falls es viele nie zugewiesene Variablen gibt).
quelle
Wie Benjamin in seiner Antwort sagte, hat TC39 ausdrücklich beschlossen, diese Funktion zumindest für ES2015 nicht aufzunehmen. Der Konsens scheint jedoch zu sein, dass sie es in ES2016 hinzufügen werden.
Die Syntax wurde noch nicht festgelegt, aber es gibt einen vorläufigen Vorschlag für ES2016, mit dem Sie statische Eigenschaften für eine Klasse deklarieren können.
Dank der Magie von Babel können Sie dies heute nutzen. Aktivieren Sie die Transformation der Klasseneigenschaften gemäß diesen Anweisungen, und Sie können loslegen. Hier ist ein Beispiel für die Syntax:
Dieser Vorschlag befindet sich in einem sehr frühen Zustand. Seien Sie also bereit, Ihre Syntax im Laufe der Zeit zu optimieren.
quelle
[Langer Thread, nicht sicher, ob er bereits als Option aufgeführt ist ...].
Eine einfache Alternative nur für Contsants wäre die Definition der Konstante außerhalb der Klasse. Dies ist nur vom Modul selbst aus zugänglich, sofern es nicht mit einem Getter geliefert wird.
Dieser Weg
prototype
ist nicht verschmutzt und Sie bekommen dieconst
.quelle
var
anstelle vonconst
2) mehrere Instanzen dieser Klasse verwenden? Dann werden die externen Variablen mit jeder Instanz der Klasse geändertES7
Syntax der Klassenmitglieder:ES7
hat eine Lösung für das "Junking" Ihrer Konstruktorfunktion. Hier ist ein Beispiel:Das obige Beispiel würde wie folgt aussehen
ES6
:Beachten Sie bei der Verwendung, dass diese Syntax möglicherweise nicht von allen Browsern unterstützt wird und möglicherweise in einer früheren Version von JS transpiliert werden muss.
Bonus: eine Objektfabrik:
quelle
Sie können das Verhalten von es6-Klassen nachahmen ... und Ihre Klassenvariablen verwenden :)
Ich habe es auf GitHub gestellt
quelle
Die Art und Weise, wie ich dies gelöst habe, ist eine weitere Option (wenn Sie jQuery zur Verfügung haben), die Felder in einem Old-School-Objekt zu definieren und dann die Klasse mit diesem Objekt zu erweitern. Ich wollte den Konstruktor auch nicht mit Aufgaben überhäufen, dies schien eine ordentliche Lösung zu sein.
- Update Nach Bergis Kommentar.
Keine JQuery-Version:
Sie haben immer noch einen "fetten" Konstruktor, aber zumindest ist alles in einer Klasse und in einem Treffer zugewiesen.
EDIT # 2: Ich habe jetzt den Kreis geschlossen und weise jetzt Werte im Konstruktor zu, z
Warum? Mit den oben genannten und einigen JSdoc-Kommentaren konnte PHPStorm die Code-Vervollständigung für die Eigenschaften durchführen. Das Zuweisen aller Variablen in einem Treffer war nett, aber die Unfähigkeit, die Eigenschaften vollständig zu codieren, ist den (mit ziemlicher Sicherheit winzigen) Leistungsvorteil nicht wert.
quelle
class
Syntax ausführen können, können Sie dies auch tunObject.assign
. Keine Notwendigkeit für jQuery.MyClassFields
einen Konstruktor zu erstellen - er hat keine Methoden. Können Sie erläutern, warum Sie dies getan haben (anstelle von beispielsweise einer einfachen Factory-Funktion, die ein Objektliteral zurückgibt)?Nun, Sie können Variablen im Konstruktor deklarieren.
quelle
Trotzdem können Sie keine Klassen wie in anderen Programmiersprachen deklarieren. Sie können jedoch beliebig viele Klassenvariablen erstellen. Das Problem ist jedoch der Umfang des Klassenobjekts. Also meiner Meinung nach Bester Weg OOP-Programmierung in ES6 Javascript: -
quelle
Dies ist eine etwas hackige Kombination aus statischer Aufladung und funktioniert für mich
anderswo verwendet
quelle
singleton_instance
als Eigenschaftsnamen empfehlen, damit jeder versteht, was Sie hier tun.