Как мне сделать «общедоступное статическое поле» в классе ES6?

86

Я создаю класс Javascript, и я хотел бы иметь общедоступное статическое поле, как в Java. Это соответствующий код:

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

Это ошибка, которую я получаю:

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

Похоже, модули ES6 не позволяют этого. Есть ли способ добиться желаемого поведения или мне нужно написать геттер?

эбабис
источник
Какую реализацию движка ECMAScript 6 вы используете?
Дай

Ответы:

136

Вы создаете «общедоступное статическое поле», используя метод доступа и ключевое слово «static»:

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

Agent.CIRCLE; // 1

Взглянув на спецификацию 14.5 - Определения классов, вы увидите что-то подозрительно уместное :)

ClassElement [Yield]:
  MethodDefinition [? Yield]
  статический MethodDefinition [? Yield];

Итак, оттуда вы можете перейти к 14.5.14 - Семантика времени выполнения: ClassDefinitionEvaluation - чтобы дважды проверить, действительно ли он делает то, что кажется. В частности, шаг 20:

  1. Для каждого ClassElement m в порядке от методов
    1. Если IsStatic of m ложно , то
      1. Пусть status будет результатом выполнения PropertyDefinitionEvaluation для m с аргументами proto и false.
    2. Иначе,
      1. Пусть status будет результатом выполнения PropertyDefinitionEvaluation для m с аргументами F и false.
    3. Если статус - внезапное завершение, то
      1. Установите LexicalEnvironment текущего контекста выполнения на lex.
      2. Статус возврата.

IsStatic определен ранее в 14.5.9.

ClassElement: static MethodDefinition
Возвращает истину.

Так PropertyMethodDefinitionвызывается с "F" (конструктор, функциональный объект) в качестве аргумента, который, в свою очередь, создает метод доступа к этому объекту. .

Это уже работает по крайней мере в IETP (техническая превью), а также в компиляторах 6to5 и Traceur.

Кангакс
источник
Для всех остальных, статические свойства средств доступа пока не поддерживаются в Node. : - / kangax.github.io/compat-table/es6/…
Дэвид Эрнандес
1
По крайней мере, в Node.js 6.x + это поддерживается.
NuSkooler
Обратите внимание, что если вы используете поток, вам нужно добавить строку unsafe.enable_getters_and_setters=trueв свой .flowconfig под [options](что раздражает).
Кристина
Это не сработает для меня, я получаю сообщение «Необработанное отклонение TypeError: невозможно установить свойство dataHashKey класса Collections {api_1 | статический get dataHashKey () {api_1 | вернуть "коллекции"; api_1 | } `` `
Паван
54

Даниэль Эренберг и Джефф Моррисон разработали 3-й этап предложения ECMAScript под названием «Функции статического класса» , который направлен на решение этой проблемы. Вместе с предложением этапа 3 «Поля классов» будущий код будет выглядеть следующим образом:

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

Вышеуказанное эквивалентно:

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 поддерживает перенос полей класса через свойства @ babel / plugin- scheme -class (включен в предустановку stage-3 ), так что вы можете использовать эту функцию, даже если ваша среда выполнения JavaScript не поддерживает ее.


По сравнению с решением @kangax по объявлению геттера это решение также может быть более производительным, поскольку здесь доступ к свойству осуществляется напрямую, а не через вызов функции.

Если это предложение будет принято, появится возможность писать код JavaScript способом, более похожим на традиционные объектно-ориентированные языки, такие как Java и C♯.


Изменить : предложение унифицированных полей класса сейчас находится на этапе 3; обновление до пакетов Babel v7.x.

Изменить (февраль 2020 г.) : функции статического класса были разделены на другое предложение. Спасибо @ GOTO0!

Тимоти Гу
источник
Я думаю, что соответствующее предложение на самом деле это (функции статического класса ).
GOTO 0
29

В текущих черновиках ECMAScript 6 (по состоянию на февраль 2015 г.) все свойства класса должны быть методами, а не значениями (обратите внимание, что в ECMAScript «свойство» аналогично концепции поля ООП, за исключением того, что значение поля должно быть Functionобъектом, а не каким-либо другое значение, такое как NumberилиObject ).

Вы по-прежнему можете указать их, используя традиционные спецификаторы свойств конструктора ECMAScript:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...
Дай
источник
11
Обратите внимание, что classсинтаксис ES6 - это просто синтаксический сахар для традиционных функций конструктора JS и прототипов.
Мэтт Браун
Я думаю, вы захотите поместить эти свойства в прототип, а не в конструктор, чтобы они были видны через ссылки на свойства из экземпляров.
Pointy
@Pointy Я сделал вывод, что OP пытается хранить константы для справки (почти как C # /. NET enum).
Дай
2
@MattBrowne Да, но для ясности classсинтаксис также имеет определенные нюансы. Например, метод, объявленный с помощью, Class.prototype.method = function () {};является перечислимым (видимым с помощью циклов for-in), в то время как classметоды не перечисляются.
Тимоти Гу
4

Чтобы получить все преимущества статической переменной, я использовал этот подход. Чтобы быть более конкретным, мы можем использовать его для использования частной переменной или наличия только общедоступного получателя или наличия обоих получателей или установщиков. В последнем случае это то же самое, что и одно из решений, опубликованных выше.

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'];

Я мог создать еще один класс, расширяющий URL, и это сработало.

Я использовал babel для преобразования моего кода ES6 в ES5

С.М. Аднан
источник
1
Что такое «полное преимущество»? Не class Url { static getQueries… }; Url.staticMember = [];было бы ничего проще?
Bergi
===Оба эти сравнения дают результаты false, кстати,
Берги
«Полное преимущество» означает, что описанным выше способом вы можете оставить _staticMember приватным, если хотите.
SM Adnan
-1

Ответ @kangax не имитирует все статическое поведение традиционного языка ООП, потому что вы не можете получить доступ к статическому свойству с его экземпляром, например const agent = new Agent; agent.CIRCLE; // Undefined

Если вы хотите получить доступ к статическому свойству так же, как ООП, вот мое решение:

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

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Проверьте код следующим образом.

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
Доступ к staticполю с помощью экземпляра был бы довольно необычным, не так ли? На некоторых языках, таких как Java, IDE фактически выдает предупреждение / подсказку, если вы делаете что-то подобное.
Isac
@ Isac Да, ты прав. Доступ по экземпляру не рекомендуется, и я тоже. Еще одна перспектива решения. 😀
legend80s