Wie erstelle ich ein "öffentliches statisches Feld" in einer ES6-Klasse?

85

Ich mache eine Javascript-Klasse und möchte ein öffentliches statisches Feld wie in Java haben. Dies ist der relevante Code:

export default class Agent {
    CIRCLE: 1,
    SQUARE: 2,
    ...

Dies ist der Fehler, den ich bekomme:

line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.

Es sieht so aus, als ob ES6-Module dies nicht zulassen. Gibt es eine Möglichkeit, das gewünschte Verhalten zu erreichen, oder muss ich einen Getter schreiben?

aebabis
quelle
Welche Implementierung der ECMAScript 6-Engine verwenden Sie?
Dai

Antworten:

136

Sie erstellen ein "öffentliches statisches Feld" mit dem Accessor und einem "statischen" Schlüsselwort:

class Agent {
    static get CIRCLE() {
      return 1;
    }
    static get SQUARE() {
      return 2;
    }
}

Agent.CIRCLE; // 1

Wenn Sie sich eine Spezifikation ansehen , 14.5 - Klassendefinitionen - würden Sie etwas verdächtig Relevantes sehen :)

ClassElement [Yield]:
  MethodDefinition [? Yield]
  statische MethodDefinition [? Yield];

Von dort aus können Sie 14.5.14 - Laufzeitsemantik: ClassDefinitionEvaluation - folgen, um zu überprüfen, ob es wirklich das tut, wie es aussieht. Insbesondere Schritt 20:

  1. Für jedes ClassElement m in der Reihenfolge von Methoden
    1. Wenn IsStatic von m falsch ist , dann
      1. Der Status sei das Ergebnis der Ausführung von PropertyDefinitionEvaluation für m mit den Argumenten proto und false.
    2. Sonst,
      1. Der Status sei das Ergebnis der Ausführung von PropertyDefinitionEvaluation für m mit den Argumenten F und false.
    3. Wenn der Status ein abrupter Abschluss ist, dann
      1. Setzen Sie die LexicalEnvironment des laufenden Ausführungskontexts auf lex.
      2. Rückgabestatus.

IsStatic wurde bereits in 14.5.9 definiert

ClassElement: static MethodDefinition Gibt
true zurück.

Wird PropertyMethodDefinitionalso mit "F" (Konstruktor, Funktionsobjekt) als Argument aufgerufen, wodurch wiederum eine Zugriffsmethode für dieses Objekt erstellt wird .

Dies funktioniert bereits in mindestens IETP (Tech Preview) sowie in 6to5- und Traceur-Compilern.

Kangax
quelle
Für alle anderen Benutzer werden die Eigenschaften des statischen Accessors in Node noch nicht unterstützt. : - / kangax.github.io/compat-table/es6/…
David Hernandez
1
Ab mindestens Node.js 6.x + wird dies unterstützt.
NuSkooler
Beachten Sie, dass Sie, wenn Sie flow verwenden, unsafe.enable_getters_and_setters=trueIhrer .flowconfig unter eine Zeile hinzufügen müssen [options](was ärgerlich ist).
Kristina
Dies funktioniert bei mir nicht. Ich erhalte eine nicht behandelte Ablehnung. TypeError: Die Eigenschaft dataHashKey der Klasse Collections {api_1 | kann nicht festgelegt werden statisch get dataHashKey () {api_1 | 'Sammlungen' zurückgeben; api_1 | } `` `
Pavan
53

Es gibt einen ECMAScript-Vorschlag der Stufe 3 mit dem Titel "Static Class Features" von Daniel Ehrenberg und Jeff Morrison, mit dem dieses Problem gelöst werden soll. Zusammen mit dem Vorschlag für Stufe 3 "Klassenfelder" sieht der zukünftige Code folgendermaßen aus:

class MyClass {
    static myStaticProp = 42;
    myProp = 42;
    myProp2 = this.myProp;
    myBoundFunc = () => { console.log(this.myProp); };

    constructor() {
        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}

Das Obige entspricht:

class MyClass {
    constructor() {
        this.myProp = 42;
        this.myProp2 = this.myProp;
        this.myBoundFunc = () => { console.log(this.myProp); };

        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}
MyClass.myStaticProp = 42;

Babel unterstützt das Transpilieren von Klassenfeldern über @ babel / plugin-comment-class-properties (in der Voreinstellung für Stufe 3 enthalten ), sodass Sie diese Funktion auch dann verwenden können, wenn Ihre JavaScript-Laufzeit dies nicht unterstützt.


Im Vergleich zu @ kangax 'Lösung, einen Getter zu deklarieren, kann diese Lösung auch leistungsfähiger sein, da hier direkt auf die Eigenschaft zugegriffen wird, anstatt eine Funktion aufzurufen.

Wenn dieser Vorschlag angenommen wird, kann JavaScript-Code auf eine Weise geschrieben werden, die herkömmlichen objektorientierten Sprachen wie Java und C♯ ähnlicher ist.


Bearbeiten : Ein Vorschlag für einheitliche Klassenfelder befindet sich jetzt in Phase 3. Update auf Babel v7.x-Pakete.

Bearbeiten (Februar 2020) : Die statischen Klassenfunktionen wurden in einen anderen Vorschlag aufgeteilt. Danke @ GOTO0!

Timothy Gu
quelle
Ich denke, der relevante Vorschlag ist tatsächlich dieser ( statische Klassenmerkmale ).
GOTO 0
29

In aktuellen Entwürfen von ECMAScript 6 (Stand Februar 2015) müssen alle Klasseneigenschaften Methoden und keine Werte sein (beachten Sie, dass in ECMAScript eine "Eigenschaft" im Konzept einem OOP-Feld ähnlich ist, außer dass der Feldwert ein FunctionObjekt sein muss, kein Objekt anderer Wert wie a Numberoder Object).

Sie können diese weiterhin mit herkömmlichen ECMAScript-Konstruktoreigenschaftsspezifizierern angeben:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...
Dai
quelle
11
Beachten Sie, dass die ES6- classSyntax ohnehin nur syntaktischer Zucker für traditionelle JS-Konstruktorfunktionen und -Prototypen ist.
Matt Browne
Ich denke, Sie möchten diese Eigenschaften auf den Prototyp und nicht auf den Konstruktor setzen, damit sie über Eigenschaftsreferenzen von Instanzen sichtbar sind.
Pointy
@Pointy Ich folgerte, dass das OP versucht, Konstanten als Referenz zu speichern (fast wie ein C # /. NET enum).
Dai
2
@MattBrowne Ja, aber um klar zu sein, hat die classSyntax auch gewisse nuancierte Unterschiede. Zum Beispiel erklärt ein Verfahren , mit Class.prototype.method = function () {};ist enumerable (sichtbar mit for-Schleifen in), während classMethoden nicht zählbare sind.
Timothy Gu
4

Um die statische Variable voll auszunutzen, folgte ich diesem Ansatz. Um genauer zu sein, können wir es verwenden, um private Variablen zu verwenden oder nur einen öffentlichen Getter zu haben oder um sowohl Getter als auch Setter zu haben. Im letzten Fall entspricht es einer der oben aufgeführten Lösungen.

var Url = (() => {
    let _staticMember = [];
    return class {
        static getQueries(hash = document.location.hash) {
            return hash;
        }

        static get staticMember(){
            return _staticMember;
        }
    };
})();

Usages:
console.log(Url.staticMember); // [];
Url.staticMember.push('it works');
console.log(Url.staticMember); // ['it works'];

Ich könnte eine weitere Klasse erstellen, die Url erweitert, und es hat funktioniert.

Ich habe babel verwendet, um meinen ES6-Code in ES5 zu konvertieren

SM Adnan
quelle
1
Was ist "voller Vorteil"? Wäre nicht class Url { static getQueries… }; Url.staticMember = [];viel einfacher gewesen?
Bergi
Diese ===Vergleiche ergeben falseübrigens
Bergi
"Voller Vorteil" bedeutet, dass Sie _staticMember wie oben beschrieben als privat halten können, wenn Sie möchten.
SM Adnan
0

Die Antwort von @kangax ahmt nicht das gesamte statische Verhalten traditioneller OOP-Sprachen nach, da Sie nicht über die Instanz wie auf die statische Eigenschaft zugreifen können const agent = new Agent; agent.CIRCLE; // Undefined

Wenn Sie wie bei OOPs auf statische Eigenschaften zugreifen möchten, ist hier meine Lösung:

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; // Late binding for inheritance
  }
}

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Testcode wie folgt.

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    console.log('this.constructor.name:', this.constructor.name); // late binding
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED;
  }
}

// Static property can be accessed by class
NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

const newApp = new NewApp;

// Static property can be accessed by it's instances
console.log('newApp.MULTIPLE_VERSIONS_SUPPORTED:', newApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Inheritance
class StandardApp extends NewApp {}

// Static property can be inherited
console.log('StandardApp.MULTIPLE_VERSIONS_SUPPORTED:', StandardApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Static property can be overwritten
StandardApp.MULTIPLE_VERSIONS_SUPPORTED = false;

const std = new StandardApp;

console.log('std.MULTIPLE_VERSIONS_SUPPORTED:', std.MULTIPLE_VERSIONS_SUPPORTED); // false

legend80s
quelle
1
Der Zugriff auf ein staticFeld durch eine Instanz wäre eher ungewöhnlich, oder? In einigen Sprachen, wie z. B. Java, geben IDEs tatsächlich eine Warnung / einen Hinweis aus, wenn Sie so etwas tun.
Isac
@Isac Ja du hast recht. Der Zugriff per Instanz wird nicht empfohlen, ebenso meine Antwort. Nur eine andere Perspektive der Lösung. 😀
legend80s