Statische Konstanten in ES6-Klassen deklarieren?

311

Ich möchte Konstanten in a implementieren class, da es dort sinnvoll ist, sie im Code zu lokalisieren.

Bisher habe ich die folgende Problemumgehung mit statischen Methoden implementiert:

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

Ich weiß, dass es eine Möglichkeit gibt, mit Prototypen herumzuspielen, aber viele empfehlen dagegen.

Gibt es eine bessere Möglichkeit, Konstanten in ES6-Klassen zu implementieren?

Jérôme Verstrynge
quelle
7
Persönlich verwende ich nur VARNAMES in Großbuchstaben und sage mir, ich soll sie nicht berühren;)
zweimal
3
@twicejr Ich denke, das ist nicht dasselbe, denn auf statische Variablen kann zugegriffen werden, ohne zuerst ein Objekt dieser Klasse zu instanziieren?
Lucas Morgan

Antworten:

385

Hier sind einige Dinge, die Sie tun könnten:

Exportieren Sie a constaus dem Modul . Abhängig von Ihrem Anwendungsfall können Sie einfach:

export const constant1 = 33;

Und importieren Sie das bei Bedarf aus dem Modul. Aufbauend auf Ihrer Idee einer statischen Methode können Sie auch einen static Get-Accessor deklarieren :

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

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

Auf diese Weise benötigen Sie keine Klammern:

const one = Example.constant1;

Babel REPL Beispiel

Dann können Sie, wie Sie sagen, da a classnur syntaktischer Zucker für eine Funktion ist, einfach eine nicht beschreibbare Eigenschaft wie folgt hinzufügen:

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

Es könnte schön sein, wenn wir einfach so etwas machen könnten:

class Example {
    static const constant1 = 33;
}

Leider ist diese Klasseneigenschaftssyntax nur in einem ES7-Vorschlag enthalten, und selbst dann kann sie nicht constzur Eigenschaft hinzugefügt werden .

CodingIntrigue
quelle
Gibt es eine Bestätigung, dass statische Eigenschaften für solche Dinge einmal berechnet werden, oder ist es sicherer, IIFE zu verwenden und die Eigenschaft manuell im IIFE hinzuzufügen, um eine wiederholte Konstruktion von Rückgabewerten zu vermeiden? Ich mache mir Sorgen, dass wenn das Ergebnis des Getters wirklich schwer ist, wie ein JSObject mit 100000 Einträgen, der arme Getter es jedes Mal erstellen muss, wenn der Getter aufgerufen wird. Es ist einfach zu testen mit performance.now/date diff, aber es kann anders implementiert werden. Es ist sicherlich einfacher, Getter als wörtliche Bewertung zu implementieren, als erweiterte Entscheidungen, ob es konstant ist oder nicht.
Dmitry
3
Während das Obige einer Klasse geschickt eine Konstanteneigenschaft hinzufügt, liegt der tatsächliche Wert für die Konstante "außerhalb" der Klassendefinition "{}", was wirklich gegen eine der Definitionen der Kapselung verstößt. Ich denke, es reicht aus, nur eine konstante Eigenschaft "innerhalb" der Klasse zu definieren, und in diesem Fall ist das get nicht erforderlich.
NoChance
1
@NoChance Gute Punkte. Das war nur zur Veranschaulichung. Es gibt keinen Grund, warum die Getter-Methode den Wert bei Bedarf nicht vollständig kapseln kann.
CodingIntrigue
Ich freue mich darauf, den ES7-Vorschlag zu verwenden, da er für mich natürlicher und der Mehrheit der OO-Sprachen gleichwertig erscheint.
Sangimed
Was möchte ich als Konstante einer Instanzvariablen deklarieren? Kann ich so etwas tunthis.defineProperty(this, 'constant1', {...})
Francesco Boi
33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Scheint für mich zu arbeiten.

Benny Jobigan
quelle
Ist dies innerhalb der Klasse mit einer normalen Methode zugänglich?
PirateApp
3
@PirateApp Sie können überall als statische Methode darauf zugreifen, auch innerhalb einer Instanz der Klasse. Da es jedoch statisch ist, das Sie nicht this.MyConstinnerhalb einer WhateverInstanz verwenden können, müssen Sie es immer so schreiben: Whatever.MyConst
TheDarkIn1978
23

Ich verwende babelund die folgende Syntax funktioniert für mich:

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

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

Bitte beachten Sie, dass Sie die Voreinstellung benötigen "stage-0".
So installieren Sie es:

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

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

Aktualisieren:

derzeit verwenden stage-3

borracciaBlu
quelle
21
Das Problem ist, dass die Konstante neu zuweisbar ist. Op will das nicht
CodingIntrigue
3
Zu stage-2
Ihrer Information
3
das sind keine Konstanten
Dave L.
1
@CodingIntrigue Würde ein Aufruf Object.freeze()der Klasse das beheben?
Antimon
1
@ Antimon Ich habe das nicht getestet, aber ich würde es mir vorstellen. Das Problem ist, dass es für alle Eigenschaften der Klasse gelten würde. Auch nicht statisch.
CodingIntrigue
14

In diesem Dokument heißt es:

Es gibt (absichtlich) keine direkte deklarative Möglichkeit, entweder Prototypdateneigenschaften (außer Methoden), Klasseneigenschaften oder Instanzeigenschaften zu definieren

Dies bedeutet, dass es absichtlich so ist.

Vielleicht können Sie eine Variable im Konstruktor definieren?

constructor(){
    this.key = value
}
DevAlien
quelle
2
Ja, das kann funktionieren. Außerdem möchte ich erwähnen, dass der Konstruktor beim Erstellen der Instanz aufgerufen wird und für jede Instanz dieser Schlüssel nicht identisch ist. Mit statischen Methoden und Eigenschaften können wir sie direkt aus der Klasse verwenden, ohne eine Instanz zu erstellen. Es gibt gute und schwache Punkte bei statischen Methoden / Eigenschaften.
Kirill Gusyatin
1
Konstanten sollten unveränderlich sein. Wenn Sie während der Erstellung Eigenschaften für das Objekt zuweisen, erhalten Sie Eigenschaften, die geändert werden können.
Philraj
11

Es ist auch möglich, ein Object.freezeObjekt der Klasse (es6) / Konstruktorfunktion (es5) zu verwenden, um es unveränderlich zu machen:

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

Der Versuch, die Klasse zu ändern, führt zu einem Soft-Fail (wirft keine Fehler, hat einfach keine Auswirkung).

rodrigo.botti
quelle
3
Dieser Soft-Fail ist für diejenigen von uns, die aus anderen Sprachen kommen, ziemlich beängstigend - nur um sich an die Idee anzupassen, dass die Tools uns beim Auffinden von Fehlern nicht viel helfen, jetzt hilft auch die Laufzeit nicht mehr. (Ansonsten gefällt mir Ihre Lösung.)
Tom
Ich liebe es Object.freeze(), Unveränderlichkeit durchzusetzen, und habe es in letzter Zeit viel benutzt. Vergessen Sie nicht, es rekursiv anzuwenden!
Jeffwtribble
6

Vielleicht setzen Sie einfach alle Ihre Konstanten in ein eingefrorenes Objekt?

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
quelle
Die statische Funktion kann die Variable 'this' nicht verwenden.
PokerFace
4

Wie https://stackoverflow.com/users/2784136/rodrigo-botti sagte, ich denke, Sie suchen Object.freeze(). Hier ist ein Beispiel für eine Klasse mit unveränderlicher Statik:

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
quelle
1

Hier ist noch eine Möglichkeit, die Sie tun können

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

Hinweis - Die Reihenfolge ist wichtig. Sie können die obigen Konstanten nicht verwenden

Verwendung console.log (Auto.CONSTANT1);

user3871424
quelle
5
Sie sind jedoch nicht unveränderlich
John Harding
1

Sie können eine Möglichkeit zum Definieren statischer Konstanten für eine Klasse mithilfe einer ungeraden Funktion von ES6-Klassen erstellen. Da Statik von ihren Unterklassen geerbt wird, können Sie Folgendes tun:

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
quelle
1

Sie können die "Konstanten" schreibgeschützt (unveränderlich) machen, indem Sie die Klasse einfrieren. z.B

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
quelle
0

Wenn Sie mit dem Mischen und Anpassen zwischen Funktions- und Klassensyntax vertraut sind, können Sie Konstanten nach der Klasse deklarieren (die Konstanten werden 'aufgehoben'). Beachten Sie, dass Visual Studio Code Schwierigkeiten hat, die gemischte Syntax automatisch zu formatieren (obwohl dies funktioniert).

class MyClass {
    // ...

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

Cam Cairns
quelle
0

Ich war das.

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

Der Wert von PI ist vor Änderungen geschützt, da er von einer Funktion zurückgegeben wird. Sie können über Circle.PI darauf zugreifen. Jeder Versuch, ihn zuzuweisen, wird einfach auf den Boden fallen gelassen, ähnlich wie der Versuch, einem Zeichenfolgenzeichen über [] zuzuweisen.

ncmathsadist
quelle
0

Sie können es folgendermaßen definieren:

class Foo {
  static MyConst = 200;

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

Sie könnten import * asSyntax verwenden. Obwohl sie keine Klasse sind, sind sie echte constVariablen.

Constants.js

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

index.js

import * as Constants from 'Constants.js'
console.log( Constants.factor );
Vincent
quelle