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.