Как сделать "публичное статическое поле" в классе ES6?


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

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

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

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

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

5 54

5 ответов:

вы делаете "публичное статическое поле" с помощью метода доступа и ключевого слова "static":

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

Agent.CIRCLE; // 1

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

ClassElement[Yield]:
  MethodDefinition[?Выход]
  статический MethodDefinition[?Выход];

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

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

IsStatic определяется ранее в 14.5.9

ClassElement : статический MethodDefinition
Вернуть true.

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

этой работает по крайней мере, в ietp (Tech preview), а также компиляторы 6to5 и Traceur.

есть Этап 3 ECMAScript предложение называется "Класс Поля" Даниэль Эренберг и Джефф Моррисон, который стремится решить эту проблему.

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


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

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


Edit: унифицированное предложение полей класса теперь на стадии 3; обновление до Babel v7.х пакетов.

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

вы все еще можете указать их с помощью традиционных описателей свойств конструктора ECMAScript:

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

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

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

ответ@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