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?


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 .


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.


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


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
}

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).


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;
        //...
    }
}

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

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


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);
  }
}

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";

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


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.


0

Sie können es folgendermaßen definieren:

class Foo {
  static MyConst = 200;

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

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 );
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.