Как создать "общедоступное статическое поле" в классе 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?
Ответы
Ответ 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