Skip to main content

Клас як вираз const A = class {}

Так, клас у JavaScript, це вираз, тому const A = class {} абсолютно легальний і створює такий самий клас, як звичайна декларація. Відрізняється лише те, як клас потрапляє в область видимості, і чи має він власне ім'я, видиме ззовні.

Теорія

TL;DR

  • Клас можна оголосити декларацією class User {} або виразом const User = class {}.
  • Клас-вираз буває анонімним і іменованим; ім'я іменованого видно лише всередині тіла класу.
  • Клас-вираз потрібен для динамічного вибору класу, фабрик класів і локальних класів усередині функції.
  • Ні декларація, ні вираз не можна використати до оголошення: обидва потрапляють у temporal dead zone.
  • Клас можна створити й використати одразу, inline: new (class { ... })().

Швидкий приклад

javascript
// Class declaration class UserA { greet() { console.log("Hello!"); } } // Class expression const UserB = class { greet() { console.log("Hello!"); } }; new UserA().greet(); // Hello! new UserB().greet(); // Hello!

Обидва варіанти створюють клас, різниця лише в тому, де і як він оголошений.

Анонімний і іменований вираз

Клас-вираз може бути анонімним або іменованим.

Анонімний:

javascript
const User = class { greet() { console.log("Hello!"); } };

Іменований:

javascript
const User = class MyUser { greet() { console.log(MyUser.name); // "MyUser" } }; const u = new User(); u.greet(); // MyUser

Ім'я MyUser видно тільки всередині самого класу, ззовні доступна лише змінна User.

javascript
console.log(typeof MyUser); // ReferenceError

Це схоже на іменовані функціональні вирази: внутрішнє ім'я зручне для рекурсивних звернень і для читабельних стек-трейсів, але воно не створює зовнішньої змінної.

Де це корисно

1. Коли клас треба створити динамічно. Наприклад, залежно від умови:

javascript
const Animal = condition ? class Dog { bark() { console.log("Woof!"); } } : class Cat { meow() { console.log("Meow!"); } }; const pet = new Animal();

2. Коли клас треба повернути з функції (фабрика класів).

javascript
function createModel(name) { return class { constructor(id) { this.id = id; this.modelName = name; } }; } const UserModel = createModel("User"); const user = new UserModel(1); console.log(user.modelName); // "User"

Дуже зручно для динамічного створення моделей, сутностей, адаптерів тощо. Той самий прийом лежить в основі міксинів: функція приймає базовий клас і повертає class extends Base { ... }.

3. Коли клас має бути локальним, усередині функції.

javascript
function makeCounter() { return class { #count = 0; inc() { this.#count++; return this.#count; } }; } const Counter = makeCounter(); const c = new Counter(); console.log(c.inc()); // 1

Клас живе тільки всередині функції й не засмічує глобальну область видимості.

Поведінка hoisting

Клас-декларація не піднімається так, як функція: ім'я класу резервується, але до рядка оголошення воно перебуває в temporal dead zone. Клас-вираз поводиться як будь-яка змінна const, тобто доступний тільки після оголошення.

javascript
new User(); // ReferenceError: Cannot access 'User' before initialization const User = class {};

Тобто в питанні hoisting обидві форми на практиці однакові: використати клас до його оголошення не можна.

Клас як значення inline

Оскільки клас, це вираз, його можна створити й одразу застосувати, не даючи імені:

javascript
console.log( new (class { sayHi() { console.log("Hello!"); } })() ); // an instance of a temporary class

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

Підсумкова таблиця

ТипПрикладДе доступне ім'яПіднімається (hoisting)?
Class Declarationclass User {}Скрізь після оголошенняНі
Anonymous Class Expressionconst A = class {}Імені немаєНі
Named Class Expressionconst A = class MyA {}Тільки всередині класуНі

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

Типові помилки

  • Думати, що ім'я іменованого класу-виразу видно ззовні. const A = class MyA {} не створює змінної MyA у зовнішній області видимості.
  • Використовувати клас до рядка оголошення. Це ReferenceError через temporal dead zone, і для декларації теж.
  • Забувати крапку з комою після виразу. const A = class {} закінчується виразом, тому пропуск ; може призвести до несподіваного продовження рядка.
  • Плутати A.name для анонімного класу. У const A = class {} рушій виведе ім'я зі змінної, тож A.name дасть "A", а не порожній рядок.
  • Створювати клас у гарячому циклі. Фабрика класів на кожен виклик створює новий клас з новим прототипом, тому екземпляри від різних викликів не проходять instanceof один одного.
  • Очікувати, що клас-вираз обходить strict mode. Тіло класу завжди strict, незалежно від форми оголошення.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.