Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Боксинг та анбоксинг у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Boxing** - це автоматичне загортання примітиву в тимчасовий об'єкт для виклику методів на ньому. Анбоксинг - зворотний процес. ```javascript "hello".toUpperCase(); // обгортка String створена, метод запущено, обгортку викинуто (42).toFixed(2); // "42.00" - те саме для Number ``` **Головне:** обгортки тимчасові. Ніколи не пиши `new String()` або `new Number()` - це постійний об'єкт, а не примітив.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Boxing** - це автоматичне загортання примітивного значення в тимчасовий об'єкт для виклику методів на ньому. Анбоксинг - зворотний процес: рушій витягує примітив назад після операції. ## Теорія ### TL;DR - Коли ти викликаєш `.toUpperCase()` на рядку, JavaScript загортає цей рядок в об'єкт `String`, запускає метод і викидає обгортку - Анбоксинг теж відбувається автоматично: об'єктна обгортка перетворюється назад у примітив під час арифметики або порівняння - Обгортка живе рівно одну операцію. Ти її не бачиш і не тримаєш на неї посилання - Ніколи не пиши `new String()` або `new Number()`. Це створює постійний об'єкт, а не примітив, і ламає `===` та `typeof` ### Короткий приклад ```javascript const str = "hello"; console.log(str.toUpperCase()); // "HELLO" // Що насправді робить рушій: // 1. Бачить виклик методу на примітиві // 2. Створює: new String("hello") <- тимчасова обгортка // 3. Викликає: wrapper.toUpperCase() // 4. Повертає: "HELLO" // 5. Викидає обгортку ``` Кожен `.trim()`, `.toFixed()`, `.charAt()` на примітиві проходить через це. Примітив залишається незмінним. Обгортка зникає. ### Як працює boxing Коли рушій бачить звернення до властивості або виклик методу на примітиві, він загортає значення у відповідний об'єкт: `String` для рядків, `Number` для чисел, `Boolean` для булевих, `Symbol` для символів. Метод виконується на цій обгортці. Результат повертається як примітив, а обгортка знищується. Сучасні рушії на кшталт V8 використовують escape analysis і в більшості випадків виконують метод без виділення реального об'єкта в пам'яті. Витрати практично нульові. Саме boxing пояснює, чому `"hello".length` працює. Рядок - не об'єкт. Але рушій створює тимчасову обгортку `String`, зчитує з неї `length` і повертає `5`. Ось і весь механізм. ### Коли зустрічається Boxing відбувається автоматично. Ти не вмикаєш його вручну і не можеш вимкнути. Важливо знати, коли це відбувається: - **Виклики методів на примітивах:** `.toUpperCase()`, `.toFixed(2)`, `.charAt(0)` - boxing це і є причина, чому вони взагалі працюють - **Доступ до властивостей примітивів:** `.length` на рядку теж викликає boxing, не тільки методи - **Відлагодження сюрпризів `typeof`:** якщо щось повертає `"object"` замість очікуваного `"string"`, хтось у кодовій базі написав `new String()` - **Уникай `new String()`, `new Number()`, `new Boolean()`** у власному коді повністю ### Типові помилки **Використання `new String()` замість рядкового літерала** ```javascript // Неправильно const a = new String("hello"); const b = new String("hello"); console.log(a === b); // false - два різні об'єкти console.log(typeof a); // "object" - не рядок // Правильно const c = "hello"; const d = "hello"; console.log(c === d); // true console.log(typeof c); // "string" ``` `new String()` створює постійний об'єкт-обгортку. Це не примітив. Порівняння ламаються. Об'єкт завжди truthy, навіть якщо рядок порожній, бо це об'єкт. Boxing, який рушій робить під час виклику методів, тимчасовий. Цей - ні. **Очікування, що властивість збережеться на примітиві** ```javascript const str = "hello"; str.customProp = "world"; console.log(str.customProp); // undefined ``` Коли ти пишеш `str.customProp = "world"`, boxing створює тимчасову обгортку і додає властивість до неї. Потім обгортка знищується. Наступного разу при зверненні до `str.customProp` boxing створює нову обгортку без жодних властивостей. Примітиви не можуть зберігати властивості. Тільки об'єкти можуть. **Порівняння примітиву з об'єктною обгорткою** ```javascript const x = 42; const y = new Number(42); console.log(x === y); // false - примітив проти об'єкта console.log(x == y); // true - нестрогий оператор спочатку розпаковує y console.log(typeof x); // "number" console.log(typeof y); // "object" ``` Змішування примітивів і об'єктних обгорток - джерело важко помітних багів. Завжди використовуй примітивні літерали. ### Де зустрічається в реальному коді - React: `props.name.toUpperCase()` боксує рядок при кожному рендері - Express: `price.toFixed(2)` перед відправкою JSON-відповіді - Колбеки масивів: `[1, 2, 3].map(n => n.toString())` боксує кожне число по одному разу - DOM: `element.getAttribute("data-id").trim()` - ланцюжок з двох операцій boxing в одному рядку - Валідація форм: `input.value.length > 0` зчитує `.length` через boxing ### Питання на співбесіді **Q:** Чому `"hello".length` працює, якщо рядки є примітивами? **A:** Boxing. Рушій загортає `"hello"` в тимчасовий об'єкт `String`, зчитує з нього властивість `length` і повертає `5`. Обгортка знищується. **Q:** Що таке анбоксинг (unboxing)? **A:** Анбоксинг - це коли об'єктна обгортка перетворюється назад у примітив. Відбувається автоматично під час арифметики: `new Number(42) + 8` спочатку розпаковує `Number` до `42`, а потім додає. Можна також викликати `.valueOf()` вручну, але на практиці це не потрібно. **Q:** Чи впливає boxing на продуктивність? **A:** У сучасному коді - ні. V8 розпізнає цей патерн і часто пропускає створення реального об'єкта в пам'яті. У рідкісних випадках об'єкт все ж виділяється, але збирач сміття прибирає його швидко. У звичайних умовах ти не побачиш boxing у профайлері. **Q:** Що повертає `typeof "hello".toUpperCase()`? **A:** `"string"`. Хоча boxing створив тимчасовий об'єкт `String` для виконання методу, `toUpperCase()` повертає примітивний рядок. `typeof` перевіряє результат, а не обгортку. **Q:** (Senior) `typeof new String("hello").toUpperCase()` також повертає `"string"`, хоча `new String("hello")` - об'єкт. Чому? **A:** Бо `toUpperCase()` завжди повертає примітивний рядок, незалежно від того, на чому його викликали. `typeof` перевіряє повернене значення, а не отримувача. Головна різниця не в типі результату, а в самому об'єкті: `new String("hello")` залишається в пам'яті як `"object"`, а обгортка від boxing - ні. Саме тому `new String` є антипатерном. ## Приклади ### Виклики методів на рядковому примітиві ```javascript const name = "alice"; const upper = name.toUpperCase(); // "ALICE" - boxing #1 const first = name.charAt(0); // "a" - boxing #2 const found = name.includes("alice"); // true - boxing #3 console.log(typeof name); // "string" - name не змінився ``` Три операції boxing, три тимчасові обгортки, три видалення. Змінна `name` залишається примітивним рядком весь час. Саме цей патерн запускається при кожному виклику рядкового методу. ### Валідація введення користувача у форм-хендлері ```javascript function processInput(input) { const trimmed = input.trim(); // boxing: загортає в String, викликає trim() const cleaned = trimmed.toLowerCase(); // boxing: загортає знову, викликає toLowerCase() const valid = cleaned.length > 0; // boxing: загортає знову, зчитує length if (valid) { return cleaned; } return null; } console.log(processInput(" Hello World ")); // "hello world" ``` Кожен виклик у ланцюжку боксує рядок наново. Переглядаючи різні кодові бази, помічаю, що розробники іноді думають, ніби такі ланцюжки створюють об'єкти, що зберігаються між викликами. Це не так. Кожна операція - це окремий цикл загортання і знищення.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.