Декларування статичних констант у класах ES6?


311

Я хочу реалізувати константи в class, тому що тут є сенс розмістити їх у коді.

Поки що я реалізую наступне рішення зі статичними методами:

class MyClass {
    static constant1() { return 33; }
    static constant2() { return 2; }
    // ...
}

Я знаю, є можливість поспілкуватися з прототипами, але багато хто рекомендує проти цього.

Чи є кращий спосіб впровадити константи в класах ES6?


7
Особисто я просто використовую великі VARNAMES і кажу собі, щоб не чіпати їх;)
Dvajr

3
@twicejr Я думаю, що це не те саме, для статичних змінних можна отримати доступ, попередньо не інстанціюючи об'єкт цього класу?
Лукас Морган

Відповіді:


385

Ось кілька речей, які ви могли зробити:

Експорт a constз модуля . Залежно від випадку використання ви можете:

export const constant1 = 33;

І імпортуйте це з модуля, де це необхідно. Або, спираючись на ідею статичного методу, ви можете оголосити static доступ аксесуара :

const constant1 = 33,
      constant2 = 2;
class Example {

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

Таким чином, вам не знадобляться дужки:

const one = Example.constant1;

Приклад відповіді Вавилон

Тоді, як ви кажете, оскільки a class- це лише синтаксичний цукор для функції, ви можете просто додати властивість, що не записується, наприклад:

class Example {
}
Object.defineProperty(Example, 'constant1', {
    value: 33,
    writable : false,
    enumerable : true,
    configurable : false
});
Example.constant1; // 33
Example.constant1 = 15; // TypeError

Це може бути приємно, якби ми могли просто зробити щось на кшталт:

class Example {
    static const constant1 = 33;
}

Але, на жаль, цей синтаксис властивостей класу є лише у пропозиції ES7, і навіть тоді він не дозволить додати constдо властивості.


чи є підтвердження того, що статичні властивості обчислюються один раз для таких речей, чи безпечніше використовувати IIFE та додавати властивість вручну в IIFE, щоб уникнути повторної побудови повернених значень. Я переживаю, що якщо результат отримання гейтера дійсно важкий, як JSObject 100000 запису, то поганому гетеру доведеться конструювати його щоразу, коли викликає геттер. Його легко перевірити на performance.now/date diff, але він може бути реалізований інакше, його, безумовно, простіше реалізувати гетерів як буквальне оцінювання, а не розширені рішення, постійні вони чи ні.
Дмитро

3
в той час як вищезазначене розумно додає константу властивості класу, фактичне значення для константи знаходиться "поза" визначення класу "{}", що дійсно порушує одне із визначень інкапсуляції. Я думаю, що достатньо просто визначити постійну властивість "всередині" класу, і в цьому випадку немає необхідності отримати get.
NoChance

1
@NoChance Гарні моменти. Це було просто показово. Немає причини, щоб метод getter не міг повністю інкапсулювати значення, якщо потрібно.
CodingIntrigue

З нетерпінням чекаю використання пропозиції ES7, тому що мені здається більш природною і рівноцінною більшості мов OO.
Сангімед

Що я хочу оголосити постійною змінною екземпляра? Чи можу я зробити щось на кшталтthis.defineProperty(this, 'constant1', {...})
Франческо Бой

33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Здається, працює для мене.


це доступно всередині класу в звичайному методі?
PirateApp

3
@PirateApp ви можете отримати доступ до нього будь-де як статичний метод, навіть із екземпляра класу. Однак, оскільки це статична статистика, яку ви не можете використовувати this.MyConstзсередини Whateverекземпляра, завжди потрібно писати так: Whatever.MyConst
TheDarkIn1978,

23

Я використовую, babelі для мене працює наступний синтаксис:

class MyClass {
    static constant1 = 33;
    static constant2 = {
       case1: 1,
       case2: 2,
    };
    // ...
}

MyClass.constant1 === 33
MyClass.constant2.case1 === 1

Зверніть увагу, що вам потрібна попередня настройка "stage-0".
Щоб встановити його:

npm install --save-dev babel-preset-stage-0

// in .babelrc
{
    "presets": ["stage-0"]
}

Оновлення:

в даний час використання stage-3


21
Проблема в тому, що константа переосмислена. Оп цього не хоче
CodingIntrigue

3
FYI, це зараз у stage-2
вавилоні

3
це не константи
Дейв Л.

1
@CodingIntrigue Чи закликає Object.freeze()клас виправити це?
Сурма

1
@Антимен Я цього не перевіряв, але я би так вважав. Проблема полягає в тому, що вона застосовуватиметься до всіх властивостей класу. Нестатичний теж.
CodingIntrigue

14

У цьому документі зазначено:

Не існує (навмисно) прямого декларативного способу визначення властивостей даних прототипу (крім методів) властивостей класу, або властивості екземпляра

Це означає, що це навмисно так.

Можливо, ви можете визначити змінну в конструкторі?

constructor(){
    this.key = value
}

2
Так, це може спрацювати. Також я хочу зазначити, що конструктор викликає, коли створений екземпляр, і для кожного екземпляра this.key буде не однаковим. Статичний метод та властивості дозволяють нам використовувати їх безпосередньо з класу, не створюючи примірників. Є хороші і слабкі місця статичних методів / властивостей.
Кирило Гусятин

1
Константи повинні бути незмінні. Присвоєння властивостей на об'єкті під час будівництва дасть властивості, які можна змінити.
philraj

11

Можна також використовувати Object.freezeдля вас об'єкт class (es6) / конструктор (es5), щоб зробити його незмінним:

class MyConstants {}
MyConstants.staticValue = 3;
MyConstants.staticMethod = function() {
  return 4;
}
Object.freeze(MyConstants);
// after the freeze, any attempts of altering the MyConstants class will have no result
// (either trying to alter, add or delete a property)
MyConstants.staticValue === 3; // true
MyConstants.staticValue = 55; // will have no effect
MyConstants.staticValue === 3; // true

MyConstants.otherStaticValue = "other" // will have no effect
MyConstants.otherStaticValue === undefined // true

delete MyConstants.staticMethod // false
typeof(MyConstants.staticMethod) === "function" // true

Спроба змінити клас дасть вам помилку (не викличе жодних помилок, це просто не матиме ефекту).


3
Цей програмний збій є досить страшним для тих, хто з інших мов - просто адаптуючись до думки, що інструменти не дуже допомагають нам у пошуку помилок, тепер навіть час виконання не допоможе. (Інакше мені подобається ваше рішення.)
Том

Я люблю Object.freeze()домагатися незмінності, і останнім часом її багато використовую. Просто не забудьте застосовувати його рекурсивно!
jeffwtribble

6

Може просто покласти всі свої константи в заморожений предмет?

class MyClass {

    constructor() {
        this.constants = Object.freeze({
            constant1: 33,
            constant2: 2,
        });
    }

    static get constant1() {
        return this.constants.constant1;
    }

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}

Статична функція не може використовувати змінну 'this'.
PokerFace

4

Як сказав https://stackoverflow.com/users/2784136/rodrigo-botti , я думаю, ви шукаєте Object.freeze(). Ось приклад класу з незмінною статикою:

class User {
  constructor(username, age) {
    if (age < User.minimumAge) {
      throw new Error('You are too young to be here!');
    }
    this.username = username;
    this.age = age;
    this.state = 'active';
  }
}

User.minimumAge = 16;
User.validStates = ['active', 'inactive', 'archived'];

deepFreeze(User);

function deepFreeze(value) {
  if (typeof value === 'object' && value !== null) {
    Object.freeze(value);
    Object.getOwnPropertyNames(value).forEach(property => {
      deepFreeze(value[property]);
    });
  }
  return value;
}

1

Ось ще один спосіб, який ви можете зробити

/*
one more way of declaring constants in a class,
Note - the constants have to be declared after the class is defined
*/
class Auto{
   //other methods
}
Auto.CONSTANT1 = "const1";
Auto.CONSTANT2 = "const2";

console.log(Auto.CONSTANT1)
console.log(Auto.CONSTANT2);

Примітка - Порядок важливий, ви не можете мати константи вище

Використовувати console.log (Auto.CONSTANT1);


5
Хоча вони і незмінні
Джон Хардінг

1

Можна створити спосіб визначення статичних констант класу за допомогою непарної функції класів ES6. Оскільки статики успадковуються за їхніми підкласами, ви можете зробити наступне:

const withConsts = (map, BaseClass = Object) => {
  class ConstClass extends BaseClass { }
  Object.keys(map).forEach(key => {
    Object.defineProperty(ConstClass, key, {
      value: map[key],
      writable : false,
      enumerable : true,
      configurable : false
    });
  });
  return ConstClass;
};

class MyClass extends withConsts({ MY_CONST: 'this is defined' }) {
  foo() {
    console.log(MyClass.MY_CONST);
  }
}

1

Ви можете зробити «константи» лише для читання (незмінні), заморозивши клас. напр

class Foo {
    static BAR = "bat"; //public static read-only
}

Object.freeze(Foo); 

/*
Uncaught TypeError: Cannot assign to read only property 'BAR' of function 'class Foo {
    static BAR = "bat"; //public static read-only
}'
*/
Foo.BAR = "wut";

0

Якщо вам зручно змішувати та співставляти між синтаксисом функції та класу, ви можете оголосити константи після класу (константи 'зняті'). Зауважте, що Visual Studio Code буде намагатися автоматично відформатувати змішаний синтаксис (хоча це працює).

class MyClass {
    // ...

}
MyClass.prototype.consts = { 
    constant1:  33,
    constant2: 32
};
mc = new MyClass();
console.log(mc.consts.constant2);    


0

Я зробив це.

class Circle
{
    constuctor(radius)
    {
        this.radius = radius;
    }
    static get PI()
    {
        return 3.14159;
    }
}

Значення PI захищене від зміни, оскільки воно повертається з функції. Ви можете отримати доступ до нього за допомогою Circle.PI. Будь-яка спроба призначити його просто падає на підлогу подібним чином до спроби присвоїти символу рядка через [].



0

Ви можете використовувати import * asсинтаксис. Хоча це не клас, вони є реальними constзмінними.

Constants.js

export const factor = 3;
export const pi = 3.141592;

index.js

import * as Constants from 'Constants.js'
console.log( Constants.factor );
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.