Come si crea un "campo statico pubblico" in una classe ES6?


86

Sto creando una classe Javascript e mi piacerebbe avere un campo statico pubblico come in Java. Questo è il codice pertinente:

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

Questo è l'errore che ottengo:

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

Sembra che i moduli ES6 non lo permettano. C'è un modo per ottenere il comportamento desiderato o devo scrivere un getter?


Quale implementazione del motore ECMAScript 6 stai utilizzando?
Dai

Risposte:


136

Si crea "campo statico pubblico" utilizzando la funzione di accesso e una parola chiave "statica":

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

Agent.CIRCLE; // 1

Guardando una specifica, 14.5 - Definizioni di classe - vedresti qualcosa di sospettosamente rilevante :)

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

Quindi da lì puoi seguire la 14.5.14 - Runtime Semantics: ClassDefinitionEvaluation - per ricontrollare se fa davvero quello che sembra. In particolare, passaggio 20:

  1. Per ogni ClassElement m in ordine dai metodi
    1. Se IsStatic di m è falso , allora
      1. Lascia che lo stato sia il risultato dell'esecuzione di PropertyDefinitionEvaluation per m con argomenti proto e false.
    2. Altro,
      1. Lascia che lo stato sia il risultato dell'esecuzione di PropertyDefinitionEvaluation per m con argomenti F e false.
    3. Se lo stato è un completamento improvviso, allora
      1. Impostare LexicalEnvironment del contesto di esecuzione in esecuzione su lex.
      2. Stato reso.

IsStatic è stato definito in precedenza nella 14.5.9

ClassElement: statico MethodDefinition
Restituisce true.

Così PropertyMethodDefinition viene chiamato con "F" (costruttore, oggetto funzione) come argomento, che a sua volta crea un metodo di accesso su quell'oggetto .

Questo funziona già in almeno IETP (anteprima Tech), così come 6to5 e compilatori Traceur.


Per chiunque altro cerchi, le proprietà della funzione di accesso statica non sono ancora supportate in Node. : - / kangax.github.io/compat-table/es6/…
David Hernandez

1
Almeno a partire da Node.js 6.x +, questo è supportato.
NuSkooler

Nota che se stai usando flow, devi aggiungere una riga unsafe.enable_getters_and_setters=trueal tuo .flowconfig sotto [options](il che è fastidioso).
kristina

Questo non funzionerà per me sto ottenendo `` Rifiuto non gestito TypeError: Impossibile impostare la proprietà dataHashKey delle raccolte di classi {api_1 | statico get dataHashKey () {api_1 | restituire "collezioni"; api_1 | } ``
Pavan

54

Esiste una proposta di Stage 3 ECMAScript chiamata "Static Class Features" di Daniel Ehrenberg e Jeff Morrison che mira a risolvere questo problema. Insieme alla proposta Stage 3 "Campi classe" , il codice futuro sarà simile a questo:

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

Quanto sopra è equivalente a:

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 supporta il transpiling dei campi della classe tramite @ babel / plugin-proposta-class-properties (incluso nel preset stage-3 ), in modo che tu possa utilizzare questa funzione anche se il tuo runtime JavaScript non la supporta.


Rispetto alla soluzione di @ kangax di dichiarare un getter, questa soluzione può anche essere più performante, poiché qui si accede direttamente alla proprietà invece che chiamando una funzione.

Se questa proposta viene accettata, sarà possibile scrivere codice JavaScript in un modo più simile ai tradizionali linguaggi orientati agli oggetti come Java e C♯.


Modifica : una proposta di campi di classe unificata è ora nella fase 3; aggiornamento ai pacchetti Babel v7.x.

Modifica (febbraio 2020) : i privilegi di classe statici sono stati suddivisi in una proposta diversa. Grazie @ GOTO0!


Penso che la proposta rilevante sia in realtà questa ( privilegi di classe statici ).
GOTO 0

29

Nelle attuali bozze di ECMAScript 6 (a partire da febbraio 2015), tutte le proprietà della classe devono essere metodi, non valori (nota in ECMAScript una "proprietà" è concettualmente simile a un campo OOP, eccetto che il valore del campo deve essere un Functionoggetto, non alcun altro valore come a Numbero Object).

Puoi ancora specificarli utilizzando i tradizionali specificatori di proprietà del costruttore ECMAScript:

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

11
Si noti che la classsintassi ES6 è comunque solo zucchero sintattico per le funzioni di costruzione JS tradizionali e prototipi.
Matt Browne

Penso che dovresti mettere quelle proprietà sul prototipo e non sul costruttore in modo che siano visibili tramite riferimenti alle proprietà dalle istanze.
Pointy

@Pointy ho dedotto che l'OP sta cercando di memorizzare le costanti per riferimento (quasi come un C # /. NET enum).
Dai

2
@ MattBrowne Sì, ma per essere chiari la classsintassi presenta anche alcune sfumate differenze. Ad esempio, un metodo dichiarato con Class.prototype.method = function () {};è enumerabile (visibile con cicli for-in), mentre i classmetodi non sono enumerabili.
Timothy Gu

4

Per ottenere il massimo vantaggio dalla variabile statica, ho seguito questo approccio. Per essere più precisi, possiamo usarlo per usare una variabile privata o avere solo getter pubblico, o avere sia getter che setter. Nell'ultimo caso è uguale a una delle soluzioni pubblicate sopra.

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

Potrei creare un'altra classe estendendo Url e ha funzionato.

Ho usato babel per convertire il mio codice ES6 in ES5


1
Cos'è il "pieno vantaggio"? Non class Url { static getQueries… }; Url.staticMember = [];sarebbe stato molto più semplice?
Bergi

Questi ===confronti hanno prodotto entrambi false, btw
Bergi

"Vantaggio completo" significa che in questo modo puoi mantenere _staticMember come privato, se lo desideri.
SM Adnan

-1

La risposta di @kangax non imita l'intero comportamento statico dei linguaggi OOP tradizionali, perché non è possibile accedere alla proprietà statica dalla sua istanza come const agent = new Agent; agent.CIRCLE; // Undefined

Se vuoi accedere alla proprietà statica proprio come l'OOP, ecco la mia soluzione:

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

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Codice di prova come segue.

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


1
L'accesso a un staticcampo da parte di un'istanza sarebbe piuttosto raro, non credi? In alcuni linguaggi, come Java, gli IDE in realtà emettono un avviso / suggerimento se fai qualcosa del genere.
Isac

@Isac Sì hai ragione. L'accesso per istanza è scoraggiato e così è la mia risposta. Solo un'altra prospettiva della soluzione. 😀
legend80s
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.