Jak utworzyć „publiczne pole statyczne” w klasie ES6?


86

Tworzę klasę Javascript i chciałbym mieć publiczne pole statyczne, takie jak w Javie. To jest odpowiedni kod:

export default class Agent {
    CIRCLE: 1,
    SQUARE: 2,
    ...

Oto błąd, który otrzymuję:

line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.

Wygląda na to, że moduły ES6 na to nie pozwalają. Czy istnieje sposób na uzyskanie pożądanego zachowania, czy też muszę napisać metodę pobierającą?


Której implementacji silnika ECMAScript 6 używasz?
Dai

Odpowiedzi:


136

Tworzysz „publiczne pole statyczne” za pomocą metody dostępu i „statycznego” słowa kluczowego:

class Agent {
    static get CIRCLE() {
      return 1;
    }
    static get SQUARE() {
      return 2;
    }
}

Agent.CIRCLE; // 1

Patrząc na specyfikację, 14.5 - Definicje klas - zobaczyłbyś coś podejrzanie trafnego :)

ClassElement [Yield]:
  MethodDefinition [? Yield]
  statyczny MethodDefinition [? Yield];

Następnie możesz przejść do punktu 14.5.14 - Semantyka środowiska uruchomieniowego: ClassDefinitionEvaluation - aby dokładnie sprawdzić, czy naprawdę działa tak, jak wygląda. Konkretnie krok 20:

  1. Dla każdego elementu ClassElement w kolejności z metod
    1. Jeśli IsStatic z m jest fałszywe , to
      1. Niech status będzie wynikiem wykonania PropertyDefinitionEvaluation dla m z argumentami proto i false.
    2. Jeszcze,
      1. Niech status będzie wynikiem wykonania PropertyDefinitionEvaluation dla m z argumentami F i false.
    3. Jeśli status jest nagłym zakończeniem, to
      1. Ustaw LexicalEnvironment kontekstu wykonywania na lex.
      2. Status zwrotu.

IsStatic jest zdefiniowany wcześniej w 14.5.9

ClassElement: static MethodDefinition
Return true.

Tak więc PropertyMethodDefinitionjest wywoływana z "F" (konstruktor, obiekt funkcji) jako argumentem, który z kolei tworzy metodę dostępową na tym obiekcie .

Działa to już co najmniej w IETP (wersja zapoznawcza), a także w kompilatorach 6to5 i Traceur.


Dla każdego, kto szuka, statyczne właściwości akcesora nie są jeszcze obsługiwane w Node. : - / kangax.github.io/compat-table/es6/…
David Hernandez

1
Jest to obsługiwane przynajmniej od wersji Node.js 6.x +.
NuSkooler

Zauważ, że jeśli używasz flow, musisz dodać linię unsafe.enable_getters_and_setters=truedo swojego .flowconfig pod [options](co jest denerwujące).
kristina

To nie zadziała. Otrzymuję komunikat `` Nieobsłużony błąd typu odrzucenia: nie można ustawić właściwości dataHashKey klasy Kolekcje {api_1 | static pobierz dataHashKey () {api_1 | return 'kolekcje'; api_1 | } ``
Pavan

54

Istnieje propozycja skryptu Stage 3 ECMAScript nazwana „Static Class Features” autorstwa Daniela Ehrenberga i Jeffa Morrisona, która ma na celu rozwiązanie tego problemu. Wraz z propozycją „Pola klas” etapu 3 przyszły kod będzie wyglądał następująco:

class MyClass {
    static myStaticProp = 42;
    myProp = 42;
    myProp2 = this.myProp;
    myBoundFunc = () => { console.log(this.myProp); };

    constructor() {
        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}

Powyższe jest równoważne z:

class MyClass {
    constructor() {
        this.myProp = 42;
        this.myProp2 = this.myProp;
        this.myBoundFunc = () => { console.log(this.myProp); };

        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}
MyClass.myStaticProp = 42;

Babel obsługuje transpiling pól klas poprzez @ babel / plugin- offer -class-properties (zawarte w ustawieniu wstępnym stage-3 ), więc możesz użyć tej funkcji, nawet jeśli twoje środowisko wykonawcze JavaScript jej nie obsługuje.


W porównaniu do rozwiązania @ kangax polegającego na deklarowaniu metody pobierającej, to rozwiązanie może być również bardziej wydajne, ponieważ tutaj dostęp do właściwości jest uzyskiwany bezpośrednio, zamiast wywoływania funkcji.

Jeśli ta propozycja zostanie zaakceptowana, możliwe będzie pisanie kodu JavaScript w sposób bardziej podobny do tradycyjnych języków obiektowych, takich jak Java i C♯.


Edycja : ujednolicona propozycja pól klas jest teraz na etapie 3; aktualizacja do pakietów Babel v7.x.

Edycja (luty 2020 r.) : Funkcje klas statycznych zostały podzielone na inną propozycję. Dzięki @ GOTO0!


Myślę, że odpowiednia propozycja jest właśnie ta ( cechy klasy statycznej ).
GOTO 0

29

W aktualnych wersjach ECMAScript 6 (stan na luty 2015), wszystkie właściwości klas muszą być metodami, a nie wartościami (uwaga w ECMAScript "właściwość" jest koncepcją podobną do pola OOP, z wyjątkiem tego, że wartość pola musi być Functionobiektem, a nie jakąkolwiek inna wartość, taka jak a Numberlub Object).

Nadal można je określić za pomocą tradycyjnych specyfikatorów właściwości konstruktora ECMAScript:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...

11
Zauważ, że classskładnia ES6 i tak jest tylko cukrem składniowym dla tradycyjnych funkcji konstruktorów JS i prototypów.
Matt Browne,

Myślę, że chciałbyś umieścić te właściwości w prototypie, a nie w konstruktorze, aby były widoczne przez odwołania do właściwości z instancji.
Pointy

@Pointy Wywnioskowałem, że OP próbuje przechowywać stałe w celach informacyjnych (prawie jak C # / .NET enum).
Dai

2
@MattBrowne Tak, ale dla jasności classskładnia ma również pewne niuanse różnic. Na przykład metoda zadeklarowana za pomocą Class.prototype.method = function () {};jest wyliczalna (widoczna w pętlach for-in), podczas gdy classmetody nie są wyliczalne.
Timothy Gu

4

Aby w pełni wykorzystać zmienną statyczną, zastosowałem to podejście. Mówiąc dokładniej, możemy go użyć do użycia zmiennej prywatnej lub posiadania tylko publicznego pobierającego, lub mając jednocześnie pobierający lub ustawiający. W ostatnim przypadku jest to to samo, co jedno z opisanych powyżej rozwiązań.

var Url = (() => {
    let _staticMember = [];
    return class {
        static getQueries(hash = document.location.hash) {
            return hash;
        }

        static get staticMember(){
            return _staticMember;
        }
    };
})();

Usages:
console.log(Url.staticMember); // [];
Url.staticMember.push('it works');
console.log(Url.staticMember); // ['it works'];

Mogłem stworzyć inną rozszerzającą klasę Url i zadziałało.

Użyłem babel do konwersji mojego kodu ES6 do ES5


1
Co to jest „pełna korzyść”? Nie class Url { static getQueries… }; Url.staticMember = [];byłoby dużo prostsze?
Bergi

Te ===porównania dają wynik false, przy okazji
Bergi

„Pełna korzyść” oznacza, że ​​w powyższy sposób możesz zachować _staticMember jako prywatne, jeśli chcesz.
SM Adnan,

-1

Odpowiedź @kangax nie naśladuje całego statycznego zachowania tradycyjnego języka OOP, ponieważ nie możesz uzyskać dostępu do statycznej właściwości przez jej instancję, taką jak const agent = new Agent; agent.CIRCLE; // Undefined

Jeśli chcesz uzyskać dostęp do właściwości statycznej, tak jak OOP, oto moje rozwiązanie:

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; // Late binding for inheritance
  }
}

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Kod testowy w następujący sposób.

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    console.log('this.constructor.name:', this.constructor.name); // late binding
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED;
  }
}

// Static property can be accessed by class
NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

const newApp = new NewApp;

// Static property can be accessed by it's instances
console.log('newApp.MULTIPLE_VERSIONS_SUPPORTED:', newApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Inheritance
class StandardApp extends NewApp {}

// Static property can be inherited
console.log('StandardApp.MULTIPLE_VERSIONS_SUPPORTED:', StandardApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Static property can be overwritten
StandardApp.MULTIPLE_VERSIONS_SUPPORTED = false;

const std = new StandardApp;

console.log('std.MULTIPLE_VERSIONS_SUPPORTED:', std.MULTIPLE_VERSIONS_SUPPORTED); // false


1
Uzyskiwanie dostępu do staticpola przez instancję byłoby raczej rzadkie, prawda? W niektórych językach, takich jak Java, IDE faktycznie wyświetla ostrzeżenie / wskazówkę, jeśli robisz coś takiego.
Isac

@Isac Tak, masz rację. Dostęp przez instancję jest odradzany, podobnie jak moja odpowiedź. Po prostu inna perspektywa rozwiązania. 😀
legenda
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.