Деструктуризація в JavaScript
Destructuring assignment (деструктуризація): синтаксис JavaScript, який витягує значення з масивів або властивості об'єктів в окремі змінні за один крок, замість кількох рядків ручного присвоєння.
Теорія
TL;DR
- Уяви що розпаковуєш посилку з підписаними комірками: береш тільки потрібне і кладеш в підписані кошики
- Деструктуризація масивів працює за позицією, а об'єктів, за назвою ключа
- Значення за замовчуванням спрацьовує тільки коли значення
undefined, неnullчи0 - Rest (
...) збирає решту елементів і завжди має стояти останнім - Використовуй коли витягуєш 2+ значення; для одного значення точкова нотація читабельніша
Швидкий приклад
// Масив: за позицією
const [first, second] = [10, 20, 30];
console.log(first); // 10
console.log(second); // 20
// Об'єкт: за назвою ключа
const user = { name: 'Alice', age: 25 };
const { name, age } = user;
console.log(name); // 'Alice'
console.log(age); // 25Двигун читає зліва направо: для масивів використовує індекси, для об'єктів шукає властивість через GetOwnProperty. Скомпільований код еквівалентний ручному присвоєнню, тому різниці у продуктивності немає.
Коли використовувати
- API-відповіді з кількома полями:
const { data, error } = await fetchUser(id) - Параметри функції з об'єкта:
function greet({ name, role = 'user' }) {} - Обмін змінними:
[a, b] = [b, a]без тимчасової змінної - React hooks:
const [count, setCount] = useState(0) - Іменовані імпорти:
import { useState, useEffect } from 'react'
Для одного значення краще пропусти. user.name читабельніше ніж const { name } = user, якщо тобі потрібне тільки воно.
Патерни для масивів
Пропуск елементів через порожній слот:
const [first, , third] = [1, 2, 3];
console.log(third); // 3Rest збирає решту:
const [head, ...tail] = [1, 2, 3, 4];
console.log(tail); // [2, 3, 4]Значення за замовчуванням спрацьовує тільки при undefined:
const [x = 5] = [0];
console.log(x); // 0 (не 5, бо 0 не є undefined)Патерни для об'єктів
Перейменування через двокрапку після ключа:
const { name: userName } = { name: 'Alice' };
console.log(userName); // 'Alice'Вкладені патерни потребують вкладеного синтаксису:
const { address: { city } } = { address: { city: 'Kyiv' } };
console.log(city); // 'Kyiv'Якщо address є undefined, це кине TypeError. Додай значення за замовчуванням або перевір через optional chaining (?.) перед деструктуризацією.
Rest для об'єктів збирає всі залишкові ключі:
const { id, ...rest } = { id: 1, name: 'Alice', age: 25 };
console.log(rest); // { name: 'Alice', age: 25 }Типові помилки
Вкладена деструктуризація кидає TypeError на undefined.
const data = {};
const { user: { name } } = data; // TypeError: Cannot read properties of undefinedВиправлення: const { user: { name } = {} } = data або data.user?.name.
Rest не може стояти не в кінці.
const [...rest, last] = [1, 2, 3]; // SyntaxErrorRest має бути останнім. [first, ...rest] працює, [...rest, last] не скомпілюється.
Неправильна назва ключа при перейменуванні.
const { userName } = { name: 'Alice' };
console.log(userName); // undefineduserName шукає ключ буквально з назвою userName. Для перейменування потрібен синтаксис { name: userName }.
Значення за замовчуванням не замінює null.
const [x = 5] = [null];
console.log(x); // null (замовчування спрацьовує тільки для undefined)Де зустрічається в реальних проектах
- React:
const [value, setValue] = useState(''), пропси компонентаfunction Button({ onClick, label }) - Express/Node.js:
const { id } = req.params,const { page = 1 } = req.query - Redux Toolkit:
const { type, payload: { id } } = action - SWR/React Query:
const { data, error } = useSWR('/api/user')
Питання на співбесіді
Q: Що виведе const [x = 1] = [0]?
A: 0. Значення за замовчуванням спрацьовує тільки коли значення undefined. Нуль є визначеним значенням.
Q: Що станеться якщо деструктурувати ключ якого немає в об'єкті?
A: Отримаєш undefined. Помилки не буде, якщо тільки не спробуєш деструктурувати властивість цього undefined.
Q: Як по-різному поводяться null і undefined зі значеннями за замовчуванням?
A: Тільки undefined активує значення за замовчуванням. null проходить як є. Це часте джерело помилок на співбесідах.
Q: Можна деструктурувати повернуте значення функції?
A: Так. const [success, data] = await fetchData() працює коли функція повертає масив.
Q: (Senior) Як V8 компілює const { x } = obj?
A: Емітує перевірку GetOwnProperty для ключа x на obj, потім присвоює результат новій прив'язці. Патерни масивів компілюються до індексованого доступу (arr[0], arr[1]). Rest використовує перебір властивостей або slice. Байткод еквівалентний ручному присвоєнню.
Приклади
Базовий: масиви та об'єкти
const coords = [10, 20, 30];
const [x, y] = coords;
console.log(x, y); // 10 20
const product = { title: 'Keyboard', price: 99, stock: 5 };
const { title, price } = product;
console.log(title, price); // 'Keyboard' 99Деструктуризація масиву бере за індексом, об'єкта за ключем. Якщо ключ не існує, отримаєш undefined, а не помилку.
Реальний сценарій: параметри функції з замовчуваннями
// Форма очікуваного вводу видна прямо в сигнатурі
function handleRequest({ params: { id }, query: { role = 'viewer', page = 1 } }) {
console.log(id, role, page);
}
handleRequest({ params: { id: '42' }, query: { role: 'admin' } });
// '42', 'admin', 1Такий патерн зустрічається в React-компонентах, Express-хендлерах і Redux-редюсерах. На практиці це найкорисніше застосування деструктуризації: бачиш що функція очікує не заходячи в тіло. Це скорочує час на розуміння незнайомого коду.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.