Dichiarare costanti statiche nelle classi ES6?


312

Voglio implementare le costanti in a class, perché è qui che ha senso individuarle nel codice.

Finora ho implementato la seguente soluzione alternativa con metodi statici:

class MyClass {
    static constant1() { return 33; }
    static constant2() { return 2; }
    // ...
}

So che esiste la possibilità di giocherellare con i prototipi, ma molti lo sconsigliano.

Esiste un modo migliore per implementare le costanti nelle classi ES6?


7
Personalmente uso solo VARNAMES maiuscoli e mi dico di non toccarli;)
volte il

3
@twicejr Penso che questo non sia lo stesso, perché è possibile accedere alle variabili statiche senza prima istanziare un oggetto di quella classe?
Lucas Morgan,

Risposte:


386

Ecco alcune cose che potresti fare:

Esporta un constdal modulo . A seconda del caso d'uso, potresti semplicemente:

export const constant1 = 33;

E importalo dal modulo dove necessario. Oppure, basandoti sulla tua idea del metodo statico, potresti dichiarare un static accessor :

const constant1 = 33,
      constant2 = 2;
class Example {

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

In questo modo, non avrai bisogno di parentesi:

const one = Example.constant1;

Esempio di REPL di Babele

Quindi, come dici tu, dal momento che a classè solo zucchero sintattico per una funzione puoi semplicemente aggiungere una proprietà non scrivibile in questo modo:

class Example {
}
Object.defineProperty(Example, 'constant1', {
    value: 33,
    writable : false,
    enumerable : true,
    configurable : false
});
Example.constant1; // 33
Example.constant1 = 15; // TypeError

Potrebbe essere carino se potessimo fare qualcosa del tipo:

class Example {
    static const constant1 = 33;
}

Ma sfortunatamente questa sintassi della proprietà della classe è solo in una proposta ES7 e anche in questo caso non consentirà l'aggiunta constalla proprietà.


c'è qualche conferma che le proprietà statiche vengono calcolate una volta per cose come questa, oppure è più sicuro usare IIFE e aggiungere la proprietà manualmente nell'IIFE per evitare la ripetuta costruzione di valori di ritorno. Sono preoccupato che se il risultato del getter è davvero pesante, come un JSObject da 100000 voci, il povero getter dovrà costruirlo ogni volta che viene chiamato getter. È facile da testare con performance.now/date diff, ma potrebbe essere implementato in modo diverso, è sicuramente più facile implementare i getter come valutazione letterale piuttosto che decisioni avanzate, sia che siano costanti o meno.
Dmitry,

3
mentre quanto sopra aggiunge abilmente una proprietà costante a una classe, il valore effettivo per la costante è "esterno" alla definizione di classe "{}", che viola davvero una delle definizioni di incapsulamento. Immagino sia sufficiente definire una proprietà costante "dentro" la classe e in questo caso non è necessario ottenere.
NoChance,

1
@NoChance Punti positivi. Era solo illustrativo. Non c'è motivo per cui il metodo getter non possa incapsulare completamente il valore, se necessario.
CodingIntrigue

Non vedo l'ora di utilizzare la proposta ES7 perché mi sembra più naturale ed equivalente alla maggior parte delle lingue OO.
Sangimed

Cosa voglio dichiarare costante una variabile di istanza? Posso fare qualcosa del generethis.defineProperty(this, 'constant1', {...})
Francesco Boi,

33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Sembra funzionare per me.


è accessibile all'interno della classe con un metodo normale?
PirateApp il

3
@PirateApp è possibile accedervi ovunque come metodo statico, anche dall'interno di un'istanza della classe. Tuttavia, poiché è statico che non è possibile utilizzare this.MyConstdall'interno di Whateverun'istanza, è sempre necessario scriverlo in questo modo: Whatever.MyConst
TheDarkIn1978,

23

Sto usando babele la seguente sintassi funziona per me:

class MyClass {
    static constant1 = 33;
    static constant2 = {
       case1: 1,
       case2: 2,
    };
    // ...
}

MyClass.constant1 === 33
MyClass.constant2.case1 === 1

Si prega di considerare che è necessario il preset "stage-0".
Per installarlo:

npm install --save-dev babel-preset-stage-0

// in .babelrc
{
    "presets": ["stage-0"]
}

Aggiornare:

attualmente in uso stage-3


21
Il problema è che la costante è riassegnabile. Op non lo vuole
CodingIntrigue

3
Cordiali saluti, ora è in babelstage-2
bmaupin il

3
quelle non sono costanti
Dave L.

1
@CodingIntrigue La chiamata Object.freeze()alla classe lo risolverebbe?
Antimonio,

1
@Antimonio Non l'ho provato ma lo penserei. Il problema è che si applicherebbe a tutte le proprietà della classe. Anche non statico.
CodingIntrigue

14

In questo documento si afferma:

Non esiste (intenzionalmente) alcun modo dichiarativo diretto per definire le proprietà della classe delle proprietà dei dati del prototipo (diverse dai metodi) o la proprietà dell'istanza

Ciò significa che è intenzionalmente così.

Forse puoi definire una variabile nel costruttore?

constructor(){
    this.key = value
}

2
Sì, questo può funzionare. Inoltre, voglio menzionare che il costruttore invoca quando viene creata l'istanza e per ogni istanza this.key non sarà la stessa. Il metodo e le proprietà statici ci consentono di usarli direttamente dalla classe, senza creare istanza. Esistono punti positivi e deboli di metodi / proprietà statici.
Kirill Gusyatin,

1
Le costanti dovrebbero essere immutabili. L'assegnazione alle proprietà dell'oggetto durante la costruzione produrrà proprietà che possono essere modificate.
philraj,

11

È anche possibile utilizzare Object.freezesull'oggetto class (es6) / funzione di costruzione (es5) per renderlo immutabile:

class MyConstants {}
MyConstants.staticValue = 3;
MyConstants.staticMethod = function() {
  return 4;
}
Object.freeze(MyConstants);
// after the freeze, any attempts of altering the MyConstants class will have no result
// (either trying to alter, add or delete a property)
MyConstants.staticValue === 3; // true
MyConstants.staticValue = 55; // will have no effect
MyConstants.staticValue === 3; // true

MyConstants.otherStaticValue = "other" // will have no effect
MyConstants.otherStaticValue === undefined // true

delete MyConstants.staticMethod // false
typeof(MyConstants.staticMethod) === "function" // true

Cercare di modificare la classe ti darà un soft-fail (non genererà errori, semplicemente non avrà alcun effetto).


3
Quel soft-fail è piuttosto spaventoso per quelli di noi che provengono da altre lingue - semplicemente adattandosi all'idea che gli strumenti non ci aiutano molto a trovare errori, ora anche il runtime non aiuterà. (Altrimenti mi piace la tua soluzione.)
Tom

Amo Object.freeze()far valere l'immutabilità e l'ho usato molto ultimamente. Basta non dimenticare di applicarlo in modo ricorsivo!
jeffwtribble,

6

Forse hai semplicemente messo tutte le tue costanti in un oggetto congelato?

class MyClass {

    constructor() {
        this.constants = Object.freeze({
            constant1: 33,
            constant2: 2,
        });
    }

    static get constant1() {
        return this.constants.constant1;
    }

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}

La funzione statica non può usare la variabile 'this'.
PokerFace,

4

Come ha detto https://stackoverflow.com/users/2784136/rodrigo-botti , penso che tu stia cercando Object.freeze(). Ecco un esempio di una classe con statica immutabile:

class User {
  constructor(username, age) {
    if (age < User.minimumAge) {
      throw new Error('You are too young to be here!');
    }
    this.username = username;
    this.age = age;
    this.state = 'active';
  }
}

User.minimumAge = 16;
User.validStates = ['active', 'inactive', 'archived'];

deepFreeze(User);

function deepFreeze(value) {
  if (typeof value === 'object' && value !== null) {
    Object.freeze(value);
    Object.getOwnPropertyNames(value).forEach(property => {
      deepFreeze(value[property]);
    });
  }
  return value;
}

1

Ecco un altro modo in cui puoi fare

/*
one more way of declaring constants in a class,
Note - the constants have to be declared after the class is defined
*/
class Auto{
   //other methods
}
Auto.CONSTANT1 = "const1";
Auto.CONSTANT2 = "const2";

console.log(Auto.CONSTANT1)
console.log(Auto.CONSTANT2);

Nota: l'ordine è importante, non è possibile avere le costanti sopra

Uso console.log (Auto.CONSTANT1);


5
Non sono immutabili però
John Harding,

1

È possibile creare un modo per definire costanti statiche su una classe utilizzando una caratteristica dispari delle classi ES6. Poiché la statica è ereditata dalle rispettive sottoclassi, è possibile effettuare le seguenti operazioni:

const withConsts = (map, BaseClass = Object) => {
  class ConstClass extends BaseClass { }
  Object.keys(map).forEach(key => {
    Object.defineProperty(ConstClass, key, {
      value: map[key],
      writable : false,
      enumerable : true,
      configurable : false
    });
  });
  return ConstClass;
};

class MyClass extends withConsts({ MY_CONST: 'this is defined' }) {
  foo() {
    console.log(MyClass.MY_CONST);
  }
}

1

Puoi rendere le "costanti" di sola lettura (immutabili) congelando la classe. per esempio

class Foo {
    static BAR = "bat"; //public static read-only
}

Object.freeze(Foo); 

/*
Uncaught TypeError: Cannot assign to read only property 'BAR' of function 'class Foo {
    static BAR = "bat"; //public static read-only
}'
*/
Foo.BAR = "wut";

0

Se ti senti a tuo agio nel mescolare e abbinare la sintassi tra funzione e classe, puoi dichiarare le costanti dopo la classe (le costanti vengono "alzate"). Si noti che Visual Studio Code farà fatica a formattare automaticamente la sintassi mista (anche se funziona).

class MyClass {
    // ...

}
MyClass.prototype.consts = { 
    constant1:  33,
    constant2: 32
};
mc = new MyClass();
console.log(mc.consts.constant2);    


0

L'ho fatto.

class Circle
{
    constuctor(radius)
    {
        this.radius = radius;
    }
    static get PI()
    {
        return 3.14159;
    }
}

Il valore di PI è protetto dalla modifica poiché è un valore restituito da una funzione. È possibile accedervi tramite Circle.PI. Qualsiasi tentativo di assegnarlo viene semplicemente lasciato cadere sul pavimento in un modo simile a un tentativo di assegnare a un carattere stringa tramite [].


0

Puoi definirlo così:

class Foo {
  static MyConst = 200;

  myFunc() {
    const doubleConst = Foo.MyConst * 2;
  }
}

0

È possibile utilizzare la import * assintassi. Sebbene non siano una classe, sono constvariabili reali .

Constants.js

export const factor = 3;
export const pi = 3.141592;

index.js

import * as Constants from 'Constants.js'
console.log( Constants.factor );
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.