Як зробити “загальнодоступне статичне поле” в класі 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 ви використовуєте?
Dai

Відповіді:


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 з m хибне , тоді
      1. Нехай статус буде результатом виконання PropertyDefinitionEvaluation для m з аргументами proto та false.
    2. Інакше,
      1. Нехай статус буде результатом виконання PropertyDefinitionEvaluation для m із аргументами F та false.
    3. Якщо статус - це різке завершення, тоді
      1. Встановіть LexicalEnvironment контексту запущеного виконання на lex.
      2. Статус повернення.

IsStatic визначено раніше в 14.5.9

ClassElement: static MethodDefinition
Повернути true.

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

Це вже працює принаймні в IETP (попередній перегляд технологій), а також у компіляторах 6to5 та Traceur.


Для тих, хто шукає, властивості статичного доступу ще не підтримуються в Node. : - / kangax.github.io/compat-table/es6/…
Девід Ернандес

1
Принаймні з Node.js 6.x + це підтримується.
NuSkooler

Зверніть увагу, що якщо ви використовуєте flow, вам потрібно додати рядок unsafe.enable_getters_and_setters=trueдо вашого .flowconfig під [options](що дратує).
Крістіна

Це не спрацює для мене, я отримую `` `Необроблене відхилення TypeError: Не вдається встановити властивість dataHashKey від колекцій класів {api_1 | статичне отримання dataHashKey () {api_1 | повернути 'колекції'; api_1 | } `` `
Паван,

54

Існує пропозиція ECMAScript на 3 етапі під назвою "Особливості статичного класу" Даніеля Еренберга та Джеффа Моррісона, яка спрямована на вирішення цієї проблеми. Разом із пропозицією 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-пропозиція-клас-властивості (включені в пресет попереднього етапу 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 та прототипів.
Matt Browne,

Я думаю, ви хотіли б розмістити ці властивості на прототипі, а не на конструкторі, щоб вони були видимими через посилання на властивості з примірників.
Пойнті

@Pointy Я зробив висновок, що OP намагається зберегти константи для довідки (майже як C # /. NET enum).
Dai

2
@MattBrowne Так, але, щоб бути зрозумілим, classсинтаксис також має певні нюансні відмінності. Наприклад, метод, оголошений за допомогою Class.prototype.method = function () {};, перелічуваний (видимий із циклами for-in), тоді як classметоди не перелічені.
Тімоті Гу

4

Щоб отримати повну перевагу статичної змінної, я дотримувався цього підходу. Якщо бути більш конкретним, ми можемо використовувати його, щоб використовувати приватну змінну або мати лише загальнодоступний getter, або мати обидва 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


1
Що таке "повна перевага"? Не class Url { static getQueries… }; Url.staticMember = [];було б набагато простіше?
Бергі

Ці ===порівняння і дають 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 Так, ти маєш рацію. Доступ за допомогою екземпляра не рекомендується, як і моя відповідь. Просто ще одна перспектива рішення. 😀
legend80s
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.