Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Клас проти функції-конструктора». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Клас це не просто інший запис функції-конструктора: у нього інша поведінка під час виклику.** Клас не можна викликати без `new` (буде `TypeError`), усе його тіло завжди працює в strict mode, його методи не перелічувані, а сам клас не піднімається (hoisting не спрацює, доступ до оголошення дає `ReferenceError`). Функція-конструктор у всьому цьому поводиться навпаки: викликається без `new` і мовчки псує дані, підкоряється звичайному режиму, її прототипні методи видно у `for...in`, і її можна викликати до оголошення. Успадкування у класах це `extends` і `super`, а у функцій це ручний ланцюжок з `Object.create` та `call`. Приватні поля `#` доступні лише класам. **Ключове:** класи безпечніші та передбачуваніші, тому в новому коді функцію-конструктор використовують лише за потреби підтримувати старе оточення.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Клас це синтаксичний цукор над прототипами, але не тільки цукор: він додає кілька жорстких правил поведінки, яких немає у функції-конструктора.** Обидва механізми зрештою створюють об'єкт із прототипом, проте клас забороняє виклик без `new`, вмикає strict mode, ховає методи від перебору і не піднімається вгору по області видимості. ## Теорія ### TL;DR - Клас неможливо викликати без `new`: це `TypeError`. Функцію-конструктор можна, і вона тихо зіпсує глобальний об'єкт або впаде. - Тіло класу завжди виконується у strict mode, навіть без `'use strict'`. - Методи класу не перелічувані, тому не з'являються у `for...in` та `Object.keys(Prototype)`. - Класи не мають hoisting: звернення до класу до оголошення дає `ReferenceError`. - Успадкування класів це `extends` і `super`, у функцій це ручні `Object.create` та `call`. - Приватні поля `#field` існують лише у класах. ### Швидкий приклад ```javascript function UserFn(name) { this.name = name; } UserFn("Alice"); // працює, але пише у глобальний об'єкт або падає у strict mode class UserClass { constructor(name) { this.name = name; } } UserClass("Alice"); // TypeError: Class constructor UserClass cannot be invoked without 'new' ``` ### Синтаксис і читабельність **Функція-конструктор:** ```javascript function User(name) { this.name = name; } User.prototype.greet = function () { console.log(`Hi, ${this.name}`); }; const user = new User("Alice"); user.greet(); ``` **Клас:** ```javascript class User { constructor(name) { this.name = name; } greet() { console.log(`Hi, ${this.name}`); } } const user = new User("Alice"); user.greet(); ``` Клас виглядає чистіше й декларативніше, особливо коли з'являється успадкування. Функція-конструктор змушує вручну прописувати `prototype`, `Object.create()` і подібне. ### Виклик без new і strict mode Функцію-конструктор можна випадково викликати без `new`, і це призведе до помилок: `this` стане `undefined` у strict-режимі або прив'яжеться до глобального об'єкта. ```javascript function User(name) { this.name = name; } const u = User("Alice"); // помилка або створить глобальну властивість window.name ``` Клас викликати без `new` не можна, це одразу виняток: ```javascript class User { constructor(name) { this.name = name; } } const u = User("Alice"); // TypeError: Class constructor User cannot be invoked without 'new' ``` Це робить класи безпечнішими. Додатково все тіло класу автоматично працює у strict mode, навіть якщо `'use strict'` ніде не написано. Функція-конструктор такої гарантії не дає: без явного `'use strict'` вона виконується у звичайному режимі. ### Перелічуваність методів, поля класу і hoisting Методи, визначені всередині `class {}`, не перелічувані, тому не з'являються під час перебору `for...in`, а методи на `prototype` функції-конструктора перелічувані: ```javascript class A { m() {} } console.log(Object.keys(A.prototype)); // [] function B() {} B.prototype.m = function() {}; console.log(Object.keys(B.prototype)); // ["m"] ``` Це робить класи «чистішими» для ітерації та серіалізації. У тілі класу не можна було оголошувати методи як звичайні функції-вирази: ```javascript class A { m = function() {}; // SyntaxError у старих версіях рушіїв } ``` Для цього потрібен синтаксис полів класу або стрілкові функції, які стандартизували пізніше (ES2022 і новіші). Нарешті, класи не мають hoisting. Функції-конструктори можна викликати до їхнього оголошення: ```javascript const a = new User(); function User() {} ``` Класи так не працюють: ```javascript const a = new User(); // ReferenceError class User {} ``` Це запобігає неочікуваним помилкам під час виконання. ### Успадкування З функціями-конструкторами доводиться писати багато зайвого: ```javascript function Animal(name) { this.name = name; } Animal.prototype.speak = function() { console.log(`${this.name} makes a sound`); }; function Dog(name) { Animal.call(this, name); } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; ``` А з класами: ```javascript class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound`); } } class Dog extends Animal { speak() { console.log(`${this.name} barks`); } } ``` Коротко і зрозуміло. До того ж клас дає `super` для звернення до реалізації батька, тоді як у функцій доводиться вручну кликати `Animal.prototype.speak.call(this)`. ### Підсумкова таблиця | Відмінність | Клас | Функція-конструктор | | --- | --- | --- | | Виклик без `new` | Помилка `TypeError` | Працює, але призводить до багів | | Strict mode | Завжди увімкнений | Немає за замовчуванням | | Hoisting | Немає | Є | | Методи перелічувані | Ні | Так | | Синтаксис успадкування | Простий (`extends`, `super`) | Складний (`Object.create`, `call`) | | Приватні поля | Є (`#field`) | Немає | | Читабельність | Висока | Низька | ### Типові помилки - Казати на співбесіді, що «клас це просто цукор», і зупинятися на цьому. Поведінкові відмінності (виклик без `new`, strict mode, hoisting, перелічуваність) якраз і перевіряють. - Плутати відсутність hoisting із повною відсутністю підйому: оголошення класу піднімається, але лежить у temporal dead zone, тому й виникає `ReferenceError`, а не `is not defined`. - Забувати `Dog.prototype.constructor = Dog` під час ручного успадкування і потім дивуватися, чому `instance.constructor` вказує на `Animal`. - Вважати `_field` у функції-конструкторі приватним. Справжня приватність є лише у полях класу з `#`. - Передавати метод класу як колбек і втрачати `this`: ні клас, ні функція-конструктор самі контекст не прив'язують.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.