Deklarujesz stałe statyczne w klasach ES6?

311

Chcę zaimplementować stałe w class , ponieważ to jest sens, aby znaleźć je w kodzie.

Do tej pory wdrażałem następujące obejście metodami statycznymi:

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

Wiem, że istnieje możliwość majstrowania przy prototypach, ale wielu nie zaleca tego.

Czy istnieje lepszy sposób na implementację stałych w klasach ES6?

Jérôme Verstrynge
źródło
7
Osobiście używam po prostu VARNAMY wielkich liter i mówię sobie, aby ich nie dotykać;)
twicejr
3
@twicejr Myślę, że to nie to samo, ponieważ można uzyskać dostęp do zmiennych statycznych bez uprzedniej instancji obiektu tej klasy?
Lucas Morgan

Odpowiedzi:

385

Oto kilka rzeczy, które możesz zrobić:

Wyeksportuj a constz modułu . W zależności od przypadku użycia możesz po prostu:

export const constant1 = 33;

W razie potrzeby zaimportuj to z modułu. Lub, bazując na Twojej idei statycznej metody, możesz zadeklarować static get accessor :

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

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

W ten sposób nie będziesz potrzebować nawiasów:

const one = Example.constant1;

Przykład Babel REPL

Następnie, jak mówisz, ponieważ a classjest po prostu cukrem syntaktycznym dla funkcji, możesz po prostu dodać właściwość, której nie można zapisać:

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

Przydałoby się coś takiego:

class Example {
    static const constant1 = 33;
}

Ale niestety ta składnia właściwości klasy jest tylko w propozycji ES7 i nawet wtedy nie pozwoli na dodanie constdo właściwości.

CodingIntrigue
źródło
czy istnieje potwierdzenie, że właściwości statyczne są obliczane jeden raz dla takich rzeczy, czy bezpieczniej jest użyć IIFE i ręcznie dodać właściwość w IIFE, aby uniknąć powtarzanej konstrukcji wartości zwracanych. Martwię się, że jeśli wynik gettera jest naprawdę ciężki, jak na przykład JSObject z wpisu 100000, wtedy biedny getter będzie musiał go skonstruować za każdym razem, gdy zostanie wywołany getter. Jest łatwy do przetestowania na podstawie performance.now/date diff, ale może być zaimplementowany inaczej, z pewnością łatwiej jest zaimplementować metody pobierające jako dosłowną ocenę niż zaawansowane decyzje, czy to na stałe, czy nie.
Dmitry
3
podczas gdy powyższe sprytnie dodaje stałą właściwość do klasy, faktyczna wartość stałej jest „poza” definicją klasy „{}”, co naprawdę narusza jedną z definicji enkapsulacji. Wydaje mi się, że wystarczy zdefiniować stałą właściwość „wewnątrz” klasy i w tym przypadku nie ma potrzeby pobierania.
NoChance,
1
@NoChance Dobre punkty. To było tylko ilustracyjne. Nie ma powodu, dla którego metoda gettera nie może w pełni zawrzeć wartości, jeśli jest to wymagane.
CodingIntrigue
Nie mogę się doczekać, aby skorzystać z propozycji ES7, ponieważ wydaje mi się ona bardziej naturalna i odpowiada większości języków OO.
Sangimed
Co chcę zadeklarować jako stała zmienną instancji? Czy mogę zrobić coś takiegothis.defineProperty(this, 'constant1', {...})
Francesco Boi
33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Wydaje się dla mnie pracować.

Benny Jobigan
źródło
czy jest to dostępne w klasie za pomocą normalnej metody?
PirateApp
3
@ PirateApp można uzyskać do niego dostęp w dowolnym miejscu jako metoda statyczna, nawet z wnętrza instancji klasy. Ponieważ jednak jest statyczny, z którego nie można korzystać this.MyConstwewnątrz Whateverinstancji, zawsze trzeba go napisać w następujący sposób: Whatever.MyConst
TheDarkIn1978
23

Używam babeli działa dla mnie następująca składnia:

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

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

Proszę wziąć pod uwagę, że potrzebujesz ustawienia wstępnego "stage-0".
Aby zainstalować:

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

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

Aktualizacja:

obecnie używam stage-3

borracciaBlu
źródło
21
Problem polega na tym, że stałą można ponownie przypisać. Op tego nie chce
CodingIntrigue
3
Do twojej wiadomości, to jest teraz w Babelstage-2
bmaupin
3
to nie są stałe
Dave L.
1
@CodingIntrigue Czy wywołanie Object.freeze()klasy to naprawi?
Antymon
1
@Antimony Nie testowałem tego, ale tak mi się wydaje. Problem polega na tym, że dotyczyłby on wszystkich właściwości klasy. Również niestatyczna.
CodingIntrigue
14

W tym dokumencie stwierdza:

Nie ma (celowo) bezpośredniego deklaratywnego sposobu zdefiniowania właściwości prototypowych właściwości danych (innych niż metody) ani właściwości instancji

Oznacza to, że tak jest celowo.

Może możesz zdefiniować zmienną w konstruktorze?

constructor(){
    this.key = value
}
DevAlien
źródło
2
Tak, to może działać. Ponadto chcę wspomnieć, że ten konstruktor wywołuje się po utworzeniu instancji i dla każdej instancji this.key nie będzie taki sam. Metoda statyczna i właściwości pozwalają nam korzystać z nich bezpośrednio z klasy, bez tworzenia instancji. Istnieją dobre i słabe punkty metod statycznych / właściwości.
Kirill Gusyatin
1
Stałe powinny być niezmienne. Przypisanie właściwości do obiektu podczas budowy da właściwości, które można modyfikować.
philraj
11

Możliwe jest również użycie Object.freezena tobie obiektu klasy (es6) / funkcji konstruktora (es5), aby stał się niezmienny:

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

Próba zmiany klasy da ci miękki błąd (nie spowoduje żadnych błędów, po prostu nie przyniesie żadnego efektu).

rodrigo.botti
źródło
3
Ta miękka awaria jest dość przerażająca dla tych z nas, którzy pochodzą z innych języków - po prostu dostosowując się do idei, że narzędzia nie pomagają nam zbytnio w znajdowaniu błędów, teraz nawet środowisko wykonawcze nie pomoże. (W przeciwnym razie podoba mi się twoje rozwiązanie.)
Tom
Uwielbiam Object.freeze()wymuszać niezmienność i ostatnio często go używam. Tylko nie zapomnij zastosować go rekurencyjnie!
jeffwtribble
6

Może po prostu umieścisz wszystkie swoje stałe w zamrożonym obiekcie?

class MyClass {

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

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

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}
aRIEL
źródło
Funkcja statyczna nie może używać zmiennej „this”.
PokerFace
4

Jak https://stackoverflow.com/users/2784136/rodrigo-botti powiedział: Myślę, że szukasz Object.freeze(). Oto przykład klasy z niezmienną statyką:

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;
}
jeffwtribble
źródło
1

Oto jeszcze jeden sposób, w jaki możesz to zrobić

/*
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);

Uwaga - kolejność jest ważna, nie możesz mieć stałych powyżej

Zastosowanie console.log (Auto.CONSTANT1);

użytkownik3871424
źródło
5
Nie są jednak niezmienne
John Harding
1

Możesz stworzyć sposób definiowania stałych statycznych w klasie, używając dziwnej cechy klas ES6. Ponieważ staty są dziedziczone przez ich podklasy, możesz wykonać następujące czynności:

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);
  }
}
TbWill4321
źródło
1

Możesz ustawić „stałe” tylko do odczytu (niezmienne), zamrażając klasę. na przykład

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";
Fraser
źródło
0

Jeśli nie masz nic przeciwko mieszaniu i dopasowywaniu składni funkcji i klasy, możesz zadeklarować stałe po klasie (stałe są „podnoszone”). Zauważ, że Visual Studio Code będzie miał problem z automatycznym formatowaniem mieszanej składni (choć działa).

class MyClass {
    // ...

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

Cam Cairns
źródło
0

Ja to zrobiłem.

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

Wartość PI jest chroniona przed zmianą, ponieważ jest to wartość zwracana z funkcji. Możesz uzyskać do niego dostęp za pośrednictwem Circle.PI. Każda próba przypisania do niego jest po prostu upuszczana na podłogę w sposób podobny do próby przypisania do znaku ciągu za pomocą [].

ncmathsadist
źródło
0

Możesz to zdefiniować w następujący sposób:

class Foo {
  static MyConst = 200;

  myFunc() {
    const doubleConst = Foo.MyConst * 2;
  }
}
zmechanic
źródło
0

Możesz użyć import * asskładni. Chociaż nie są klasą, są prawdziwymi constzmiennymi.

Constants.js

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

index.js

import * as Constants from 'Constants.js'
console.log( Constants.factor );
Vincent
źródło