Hoe maak ik een “openbaar statisch veld” in een ES6-klasse?

Ik ben een Javascript-klasse aan het maken en ik zou graag een openbaar statisch veld willen hebben, zoals in Java. Dit is de relevante code:

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

Dit is de foutmelding die ik krijg:

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

Het lijkt erop dat ES6-modules dit niet toestaan. Is er een manier om het gewenste gedrag te krijgen of moet ik een getter schrijven?


Antwoord 1, autoriteit 100%

U maakt “openbaar statisch veld” met behulp van accessor en een “statisch” zoekwoord:

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

Kijkend naar een specificatie, 14,5— Class Definitions — you’ d iets verdacht relevant zien 🙂

KlasseElement[Opbrengst] :
  MethodeDefinitie[?Opbrengst]
  statischMethodDefinition[?Opbrengst] ;

Dus vanaf daar kun je volgen naar 14.5.14— Runtime-semantiek: ClassDefinitionEvaluation — om te controleren of het echt doet wat het lijkt te doen. Specifiek, stap 20:

  1. Voor elk ClassElement m in volgorde van methoden
    1. Als IsStatic van m onwaar is, dan
      1. Laat status het resultaat zijn van het uitvoeren van PropertyDefinitionEvaluation voor m met argumenten proto en false.
    2. Anders,
      1. Laat status het resultaat zijn van het uitvoeren van PropertyDefinitionEvaluation voor m met argumenten F en false.
    3. Als status een abrupte voltooiing is, dan
      1. Stel de LexicalEnvironment van de actieve uitvoeringscontext in op lex.
      2. Retourstatus.

IsStatic is eerder gedefinieerd in 14.5.9

ClassElement: statische MethodDefinition
Retourneer waar.

Dus PropertyMethodDefinitionwordt aangeroepen met “F” (constructor, function object) als argument, dat op zijn beurt maakt een accessor-methode voor dat object.

Deze werkt alin ten minste IETP (tech preview), evenals 6to5 en Traceur compilers.


Antwoord 2, autoriteit 42%

Sinds ECMAScript 2022 kun je zoiets als dit doen, vergelijkbaar met traditionele klassengeoriënteerde talen zoals Java en C#:

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'
    }
}

Het bovenstaande is gelijk aan:

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;

De functies zijn toegevoegd in de “Static Class Features”en “Class Fields”voorstellen door Daniel Ehrenberg et al. Google Chrome (en nieuwe Edge) begon beide voorstellen te ondersteunen in versie 72, gelijk aan Node.js 12+. Firefox ondersteunt openbare instantievelden sinds versie 69en statische instantievelden sinds versie 75. Safari ondersteunt beide sinds versie 14.1. Zie meer informatie op caniuse.com.

Voor oudere browsers die deze functies nog niet ondersteunen, kunt u Babelgebruiken om transpilerer klassenvelden. Dit vereist @babel/plugin-proposal-class-propertiesmoet worden ingeschakeld (standaard ingeschakeld in @babel/plugin-env vanaf v7 .14.0).


Vergeleken met de oplossing van @kangax om een ​​getter te declareren, kan deze oplossing ook beter presteren, omdat hier rechtstreeks toegang wordt verkregen tot de eigenschap in plaats van door een functie aan te roepen.


Bewerken: een voorstel voor uniforme klassenvelden bevindt zich nu in fase 3.

Bewerken (februari 2020): de statische klassefuncties zijn opgesplitst in een ander voorstel. Bedankt @GOTO0!

Bewerken (maart 2021): met uitzondering van Safari, ondersteunen alle belangrijke browsers die na april 2020 zijn uitgebracht deze functie nu!

Bewerken (juni 2021): beide voorstellen worden geaccepteerd door TC39, de ECMAScript taalcommissie, en Safari heeft deze functie in versie 14.1 geleverd!


Antwoord 3, autoriteit 20%

In de huidige concepten van ECMAScript 6 (vanaf februari 2015) moeten alle klasse-eigenschappen methoden zijn, geen waarden (merk op dat in ECMAScript een “eigenschap” qua concept vergelijkbaar is met een OOP-veld, behalve dat de veldwaarde een Functionobject, geen andere waarde zoals een Numberof Object).

U kunt deze nog steeds specificeren met behulp van traditionele ECMAScript-constructoreigenschapspecificaties:

class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...

Antwoord 4, autoriteit 3%

Om volledig te profiteren van de statische variabele, volgde ik deze aanpak. Om specifieker te zijn, kunnen we het gebruiken om een ​​privévariabele te gebruiken of om alleen een openbare getter te hebben, of om zowel een getter als een setter te hebben. In het laatste geval is het hetzelfde als een van de oplossingen die hierboven zijn gepost.

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'];

Ik zou een andere klasse kunnen maken die Url uitbreidt en het werkte.

Ik heb babel gebruikt om mijn ES6-code naar ES5 te converteren


Antwoord 5

@kangax’s antwoord imiteert niet het hele statische gedrag van traditionele OOP-talen, omdat je geen toegang hebt tot de statische eigenschap door zijn instantie zoals const agent = new Agent; agent.CIRCLE; // Undefined

Als je toegang wilt tot statische eigenschappen, net als die van OOP, dan is hier mijn oplossing:

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

Testcode als volgt.

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

Other episodes