Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «return усередині конструктора». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Конструктор і без `return` повертає `this`, а явний `return` має значення лише тоді, коли повертає об'єкт.** Об'єкт підміняє щойно створений екземпляр і саме він потрапляє до змінної після `new`. Примітив (число, рядок, `true`, `null`) повністю ігнорується, і `new` усе одно віддає `this`. У класі з `extends` виклик `super()` обов'язковий ще до будь-якого `return`. ```javascript class User { constructor(name) { this.name = name; return { custom: "another object" }; } } console.log(new User("Tim")); // { custom: "another object" } ``` **Ключове:** об'єкт із конструктора підміняє `this`, примітив ігнорується. На цьому тримаються Singleton і фабрики.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`return` у конструкторі потрібен лише для того, щоб підмінити створюваний екземпляр іншим об'єктом.** Без нього конструктор неявно повертає `this`, а повернений примітив рушій просто ігнорує. ## Теорія ### TL;DR - `return` у конструкторі не обов'язковий: за замовчуванням повертається `this`. - Якщо повернути об'єкт, він замінює `this` і саме він стає результатом `new`. - Якщо повернути примітив, його ігноровано, результатом усе одно буде `this`. - У класі-нащадку `super()` треба викликати до будь-якого `return`, інакше `ReferenceError`. - Цю поведінку використовують шаблони Singleton, Factory та Proxy. ### Швидкий приклад ```javascript class User { constructor(name) { this.name = name; } } const u = new User("Tim"); console.log(u); // User { name: "Tim" } ``` ### Звичайна поведінка конструктора Коли ви пишете `new User("Tim")`, рушій робить приблизно таке: 1. Створює новий порожній об'єкт `{}`. 2. Встановлює прототипом цього об'єкта `User.prototype`. 3. Викликає `constructor`, де `this` посилається на цей новий об'єкт. 4. Повертає `this` з конструктора за замовчуванням. Тобто якщо ви нічого не повертаєте, `return this` виконується неявно. ### Повернення об'єкта підміняє this Якщо `return` віддає об'єкт, він замінює створений екземпляр: ```javascript class User { constructor(name) { this.name = name; return { custom: "another object" }; } } const u = new User("Tim"); console.log(u); // { custom: "another object" } ``` Зверніть увагу: `this.name = name` усе одно виконалося, просто той об'єкт нікуди не потрапив. Повернений об'єкт не має ані `User.prototype`, ані методів класу, тож `u instanceof User` буде `false`. ### Повернення примітива ігнорується Числа, рядки, булеві значення та інші примітиви не можуть перезаписати `this`: ```javascript class User { constructor(name) { this.name = name; return 42; } } const u = new User("Tim"); console.log(u); // User { name: "Tim" } ``` Помилки не буде, значення просто відкидається. Це стосується і `null`: він примітив, тому теж ігнорується, а от `return undefined` нічого не змінює за визначенням. ### Навіщо це потрібно: Singleton і фабрики Можливість повернути свій об'єкт існує саме для того, щоб конструктор міг віддати щось особливе. Класичний приклад, Singleton, тобто один спільний екземпляр на всі виклики: ```javascript class Database { constructor() { if (Database.instance) { return Database.instance; // always hands back the same object } this.connected = true; Database.instance = this; } } const a = new Database(); const b = new Database(); console.log(a === b); // true ``` Тут `return this` не потрібен, бо рушій робить це сам, а `return Database.instance` підміняє новий екземпляр наявним. Так само працює конструктор, який залежно від вхідних даних віддає різні об'єкти: ```javascript class Example { constructor(x) { this.x = x; if (x > 10) return { value: "too large" }; } } console.log(new Example(5)); // Example { x: 5 } console.log(new Example(20)); // { value: "too large" } ``` ### return у класах-нащадках У класі без `extends` і в класі з `extends` правило те саме: повернений об'єкт замінює `this`. ```javascript class A {} class B extends A { constructor() { super(); return { name: "replacement" }; } } const b = new B(); console.log(b); // { name: "replacement" } ``` Але `super()` лишається обов'язковим навіть тоді, коли ви повертаєте власний об'єкт: ```javascript class A {} class B extends A { constructor() { // error, even though there is a return return { test: 1 }; } } new B(); // ReferenceError: Must call super constructor before returning from derived constructor ``` **Правила в одному місці:** | Сценарій | Що повертається | | --- | --- | | Немає `return` | `this`, тобто створений екземпляр | | `return this` | той самий екземпляр | | `return { ... }` | цей об'єкт замість `this` | | `return 42` / `"hi"` / `true` | ігнорується, повертається `this` | | Нащадок без `super()` | помилка: `super()` має бути до `return` | ### Типові помилки - Повернення об'єктного літерала «для зручності» і подальше здивування, що `instanceof` та методи класу зникли: підмінений об'єкт не має прототипу класу. - Очікування, що `return 42` зробить `new User()` числом. Примітиви з конструктора ігноруються без жодного попередження. - Ранній `return` у конструкторі-нащадку до `super()`. Це `ReferenceError`, а не тихий вихід. - Singleton через `return Database.instance` разом із важкою ініціалізацією до цієї перевірки: робота виконується зайвий раз, хоча результат викидається. - Плутанина з `null`: це примітив, тому `new` віддасть `this`, а не `null`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.