Подтвердить что ты не робот

Как создать "общедоступное статическое поле" в классе 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 этого не позволяют. Есть ли способ получить желаемое поведение или мне нужно написать getter?

4b9b3361

Ответ 1

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

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

Agent.CIRCLE; // 1

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

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

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

  • Для каждого класса Class m в порядке от методов
    • Если IsStatic of m является false, тогда
      • Пусть статус является результатом выполнения PropertyDefinitionEvaluation для m с аргументами proto и false.
    • В противном случае,
      • Пусть статус - результат выполнения PropertyDefinitionEvaluation для m с аргументами F и false.
    • Если состояние является резким завершением, тогда
      • Задайте исполняемые контексты выполнения LexicalEnvironment для lex.
      • Статус возврата.

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

ClassElement: static MethodDefinition
  Верно true.

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

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

Ответ 2

Существует предложение ECMAScript Stage 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-proposal-class-properties (включен в предварительный старт-3), чтобы вы могли использовать эту функцию, даже если ваша среда выполнения JavaScript не поддерживает ее.


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

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


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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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