Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Клас як вираз const A = class {}». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Так, клас, це повноцінний вираз: `const A = class {}` створює точно такий самий клас, як і `class A {}`, різниця лише в тому, де і як він оголошений.** Клас-вираз може бути анонімним (`const User = class {}`) або іменованим (`const User = class MyUser {}`), причому ім'я `MyUser` видно тільки всередині тіла класу, а ззовні доступна лише змінна `User`. Такий запис потрібен, коли клас треба створити динамічно (тернарний вибір між двома класами), повернути з функції-фабрики або тримати локальним усередині функції, не засмічуючи глобальну область видимості. Використовувати його до рядка оголошення не можна: `const` лежить у temporal dead zone. ```javascript function createModel(name) { return class { constructor(id) { this.id = id; this.modelName = name; } }; } const UserModel = createModel("User"); console.log(new UserModel(1).modelName); // "User" ``` **Ключове:** так, `const A = class {}` цілком законний, це зручно для динамічних, локальних і тимчасових класів, а ім'я в ньому може бути локальним або взагалі відсутнім.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Так, клас у 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 Declaration** | `class User {}` | Скрізь після оголошення | Ні | | **Anonymous Class Expression** | `const A = class {}` | Імені немає | Ні | | **Named Class Expression** | `const 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, незалежно від форми оголошення.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.