Пошук уроків, статей та іншого контенту
Розглянете деструктуризацію, rest і пропуск елементів для зручного розпакування значень масиву.
Деструктуризація масивів — це спосіб отримати значення з масиву та одночасно присвоїти їх окремим змінним.
const colors = ["red", "green", "blue"];
const [first, second, third] = colors;
console.log(first); // "red"
console.log(second); // "green"
console.log(third); // "blue"Порядок змінних відповідає порядку елементів:
const values = [10, 20, 30];
const [a, b, c] = values;
console.log(a); // 10
console.log(b); // 20
console.log(c); // 30Деструктуризація не змінює початковий масив. Вона лише читає його значення.
const source = [1, 2, 3];
const [first] = source;
console.log(source); // [1, 2, 3]
console.log(first); // 1Якщо змінних більше, ніж елементів, відсутні значення отримують undefined:
const numbers = [5, 10];
const [first, second, third] = numbers;
console.log(third); // undefinedЯкщо змінних менше, зайві елементи просто не використовуються:
const numbers = [5, 10, 15];
const [first] = numbers;
console.log(first); // 5Елемент можна пропустити, залишивши порожнє місце між комами:
const [first, , third] = ["a", "b", "c"];
console.log(first); // "a"
console.log(third); // "c"Так можна пропускати один або кілька елементів:
const values = [10, 20, 30, 40, 50];
const [, second, , fourth] = values;
console.log(second); // 20
console.log(fourth); // 40Початкові та кінцеві коми також можуть бути корисними для явного опису позицій:
const [, , third] = ["a", "b", "c"];
console.log(third); // "c"Пропуск елементів особливо зручний, коли API повертає масив із фіксованою структурою, але потрібне лише одне значення.
const response = ["success", 200, { cached: true }];
const [, statusCode] = response;
console.log(statusCode); // 200Водночас надто багато пропусків погіршує читабельність. Якщо потрібне значення розташоване далеко в масиві, інколи краще спочатку надати масиву зрозумілу структуру або використати об’єкт.
Для змінної можна вказати значення за замовчуванням:
const [name = "Невідомий користувач", age = 0] = [];
console.log(name); // "Невідомий користувач"
console.log(age); // 0Значення за замовчуванням використовується лише тоді, коли відповідний елемент дорівнює undefined:
const values = [undefined, null, 0, false];
const [
first = "default",
second = "default",
third = 100,
fourth = true,
] = values;
console.log(first); // "default"
console.log(second); // null
console.log(third); // 0
console.log(fourth); // falsenull не запускає значення за замовчуванням, оскільки null — це окреме явне значення.
Значення за замовчуванням може бути виразом:
function createId() {
return Math.floor(Math.random() * 1000);
}
const [id = createId()] = [];
console.log(id);Такий вираз обчислюється лише тоді, коли елемент має значення undefined.
let calls = 0;
function getDefault() {
calls += 1;
return "default";
}
const [first = getDefault()] = ["actual"];
const [second = getDefault()] = [];
console.log(first); // "actual"
console.log(second); // "default"
console.log(calls); // 1Оператор ... у деструктуризації масиву збирає решту елементів у новий масив:
const numbers = [1, 2, 3, 4, 5];
const [first, ...rest] = numbers;
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]Перед rest можна отримати кілька початкових елементів:
const [first, second, ...remaining] = [10, 20, 30, 40];
console.log(first); // 10
console.log(second); // 20
console.log(remaining); // [30, 40]rest завжди має бути останнім елементом шаблону:
const [first, ...middle, last] = [1, 2, 3];
// SyntaxErrorОдин шаблон деструктуризації може містити лише один rest:
const [first, ...rest, ...anotherRest] = [1, 2, 3];
// SyntaxErrorrest створює новий масив, а не повертає посилання на частину початкового:
const source = [1, 2, 3, 4];
const [first, ...rest] = source;
rest.push(5);
console.log(source); // [1, 2, 3, 4]
console.log(rest); // [2, 3, 4, 5]Однак деструктуризація не робить глибоку копію вкладених об’єктів:
const source = [
{ name: "Alice" },
{ name: "Bob" },
];
const [...copy] = source;
copy[0].name = "Updated";
console.log(source[0].name); // "Updated"Новим є зовнішній масив, але об’єкти всередині залишаються спільними.
Деструктуризацію можна виконувати не лише під час оголошення змінних, а й під час повторного присвоєння:
let first = 0;
let second = 0;
[first, second] = [10, 20];
console.log(first); // 10
console.log(second); // 20Якщо такий вираз починається з квадратних дужок і стоїть там, де JavaScript очікує інструкцію, синтаксис коректний.
Під час присвоєння об’єктних властивостей або іншого складнішого виразу дужки можуть бути необхідними:
const user = {
firstName: "Olena",
lastName: "Koval",
};
let firstName;
let lastName;
({ firstName, lastName } = user);Для масивів найчастіше достатньо такого запису:
let x;
let y;
[x, y] = [1, 2];Деструктуризація дає змогу обміняти значення двох змінних без проміжної змінної:
let left = "ліва сторона";
let right = "права сторона";
[left, right] = [right, left];
console.log(left); // "права сторона"
console.log(right); // "ліва сторона"Цей прийом працює тому, що права частина спочатку обчислюється як масив із поточними значеннями, а вже потім значення присвоюються змінним.
Шаблон може містити інші шаблони. Це дає змогу розпаковувати вкладені масиви:
const coordinates = [10, [20, 30]];
const [x, [y, z]] = coordinates;
console.log(x); // 10
console.log(y); // 20
console.log(z); // 30Вкладена деструктуризація може поєднуватися з пропуском і значеннями за замовчуванням:
const data = [
"ready",
[1920, 1080],
["light"],
];
const [
status,
[width, height],
[theme = "dark", language = "uk"],
] = data;
console.log(status); // "ready"
console.log(width); // 1920
console.log(height); // 1080
console.log(theme); // "light"
console.log(language); // "uk"Якщо проміжний елемент відсутній, деструктуризація вкладеної структури може спричинити помилку:
const data = [1, undefined];
// const [first, [second]] = data;
// TypeError: undefined is not iterableЗначення за замовчуванням можна призначити всьому вкладеному масиву:
const data = [1];
const [first, [second] = [100]] = data;
console.log(first); // 1
console.log(second); // 100Це відрізняється від значення за замовчуванням для внутрішнього елемента:
const data = [1, []];
const [first, [second = 100]] = data;
console.log(second); // 100У першому прикладі відсутній сам вкладений масив, а в другому вкладений масив існує, але не містить першого елемента.
Масив можна деструктурувати безпосередньо в параметрах функції:
function calculateTotal([price, quantity = 1]) {
return price * quantity;
}
console.log(calculateTotal([25, 3])); // 75
console.log(calculateTotal([40])); // 40Це зручно, коли функція приймає масив із визначеним форматом:
function formatPoint([x, y, ...metadata]) {
return {
x,
y,
metadata,
};
}
console.log(formatPoint([10, 20, "red", "selected"]));
// {
// x: 10,
// y: 20,
// metadata: ["red", "selected"]
// }Якщо параметр може бути пропущений, слід задати значення за замовчуванням для всього параметра:
function getFirst([first] = []) {
return first;
}
console.log(getFirst([10, 20])); // 10
console.log(getFirst()); // undefinedБез = [] виклик getFirst() завершився б помилкою, оскільки JavaScript не може деструктурувати undefined.
Деструктуризація масивів працює не лише зі справжніми масивами. Вона використовує протокол ітерації, тому джерелом можуть бути інші ітерабельні значення:
рядки;
Set;
генератори;
інші об’єкти з методом Symbol.iterator.
const [first, second, third] = "JS!";
console.log(first); // "J"
console.log(second); // "S"
console.log(third); // "!"Приклад із Set:
const uniqueValues = new Set(["a", "b", "c"]);
const [first, ...rest] = uniqueValues;
console.log(first); // "a"
console.log(rest); // ["b", "c"]Об’єкт, який не є ітерабельним, не можна деструктурувати як масив:
const object = { 0: "a", 1: "b", length: 2 };
// const [first] = object;
// TypeError: object is not iterableНаявність числових властивостей і length сама по собі не робить об’єкт ітерабельним.
Під час деструктуризації генератор виконується послідовно:
function* createNumbers() {
yield 10;
yield 20;
yield 30;
}
const [first, , third] = createNumbers();
console.log(first); // 10
console.log(third); // 30Деструктуризація споживає значення ітератора. Якщо зберегти генератор і спробувати прочитати його ще раз, початкові значення вже можуть бути недоступними:
function* createValues() {
yield "a";
yield "b";
yield "c";
}
const iterator = createValues();
const [first] = iterator;
const [second] = iterator;
console.log(first); // "a"
console.log(second); // "b"undefinedПропуск елемента не створює змінну:
const [, second] = [10, 20];
console.log(second); // 20Якщо змінну потрібно створити, але залишити для неї значення undefined, її можна записати явно:
const [first, second] = [10, undefined];
console.log(first); // 10
console.log(second); // undefinedЦе важливо під час використання значень за замовчуванням:
const [, value = 100] = [10];
console.log(value); // 100Тут другий елемент відсутній, тому змінна value отримує 100.
Змінні, оголошені в шаблоні деструктуризації через let або const, не можна використовувати до завершення оголошення:
// const [first = second, second = 10] = [];
// ReferenceErrorПід час обчислення значення за замовчуванням second ще перебуває в тимчасовій мертвій зоні.
Змінна, оголошена раніше, може використовуватися у значенні за замовчуванням:
const defaultValue = 42;
const [value = defaultValue] = [];
console.log(value); // 42Варто також пам’ятати, що var має іншу поведінку через підняття оголошення, але для сучасного коду краще використовувати const і let.
Нижче функція аналізує список оцінок:
перший елемент — ім’я студента;
другий — основна оцінка;
решта — додаткові оцінки;
якщо основної оцінки немає, використовується 0.
function summarizeStudent([name = "Невідомий", mainScore = 0, ...extraScores]) {
const allScores = [mainScore, ...extraScores];
const total = allScores.reduce((sum, score) => sum + score, 0);
const average = total / allScores.length;
return {
name,
mainScore,
extraScores,
average,
};
}
const student = summarizeStudent(["Iryna", 90, 85, 100]);
console.log(student);
// {
// name: "Iryna",
// mainScore: 90,
// extraScores: [85, 100],
// average: 91.66666666666667
// }
console.log(summarizeStudent(["Maksym"]));
// {
// name: "Maksym",
// mainScore: 0,
// extraScores: [],
// average: 0
// }У цьому прикладі параметр одразу деструктурується, значення за замовчуванням захищає від неповних даних, а rest зберігає всі додаткові оцінки.
rest стоїть не в кінці// const [...items, last] = [1, 2, 3];
// SyntaxErrorПравильно:
const [first, ...items] = [1, 2, 3];undefined або null// const [first] = undefined;
// TypeError
// const [second] = null;
// TypeErrorЯкщо джерело може бути відсутнім, використовуйте значення за замовчуванням для всього масиву:
const [first] = undefined ?? [];
console.log(first); // undefinedДля параметра функції:
function readFirst(values = []) {
const [first] = values;
return first;
}
console.log(readFirst()); // undefinednull і undefinedconst [value = 10] = [null];
console.log(value); // nullЗначення 10 не застосовується, бо значення null є явно переданим.
const record = { first: "a", second: "b" };
// const [first, second] = record;
// TypeErrorДля об’єктів потрібна деструктуризація об’єктів:
const { first, second } = record;
console.log(first); // "a"
console.log(second); // "b"Такий код може бути формально правильним, але важким для читання:
const [
[
,
{
settings: [, value = 0],
},
],
] = data;Якщо структура даних складна або нестабільна, краще розбити операцію на кілька кроків або змінити формат даних. Деструктуризація має спрощувати код, а не приховувати логіку.
const original = [[1, 2]];
const [copy] = original;
copy.push(3);
console.log(original); // [[1, 2, 3]]Деструктуризація лише дістає посилання на вкладений масив. Для глибокого копіювання потрібен окремий підхід, наприклад structuredClone, якщо він доступний у цільовому середовищі.
Деструктуризація доречна, коли:
позиції елементів мають чітке значення;
потрібно швидко отримати перші або останні частини даних;
треба пропустити непотрібні позиції;
функція приймає кортеж або масив із визначеним форматом;
потрібно розділити перше значення і решту;
потрібно обміняти значення змінних.
Варто бути обережним, коли:
структура масиву часто змінюється;
потрібні значення визначаються не позицією, а назвою;
у шаблоні багато пропусків;
вкладеність стає надмірною;
джерелом може бути null або undefined.
У таких випадках об’єкт із названими властивостями часто краще передає структуру даних.
Деструктуризація масивів розпаковує значення за їхніми позиціями.
Кома без змінної дає змогу пропустити елемент.
Значення за замовчуванням використовуються лише для undefined.
...rest збирає всі непрочитані елементи в новий масив і має бути останнім.
Деструктуризація може бути вкладеною та використовуватися в параметрах функцій.
Працюють не лише масиви, а й інші ітерабельні значення.
Деструктуризація не створює глибоких копій вкладених об’єктів.
Для null і undefined потрібно передбачати значення за замовчуванням або перевірку.
Якщо позиційна структура стає незрозумілою, краще використати об’єкт або спростити формат даних.