Оператор spread і rest
Оператор ... це один синтаксис із двома ролями: spread (розпакування) розгортає ітерований об'єкт або властивості об'єкта на окремі елементи, а rest (залишок) збирає решту елементів в один масив чи об'єкт. Роль визначає не сам оператор, а місце, де він стоїть: у літералі та у виклику функції це spread, у параметрах функції та в деструктуризації це rest.
Теорія
TL;DR
...у літералі або виклику розгортає значення:[...arr],{...obj},sum(...nums)....у деструктуризації або в списку параметрів збирає залишок:[a, ...rest],function fn(...args).- Spread робить поверхневу копію (shallow copy): вкладені об'єкти та масиви копіюються за посиланням.
- Spread по об'єктах зливає властивості, і пізніший ключ перезаписує раніший.
- Це сучасна заміна
concat,Object.assignіArray.prototype.slice.call(arguments).
Швидкий приклад
const numbers = [1, 2, 3];
const moreNumbers = [0, ...numbers, 4, 5]; // spread: розпакували масив
console.log(moreNumbers); // [0, 1, 2, 3, 4, 5]
const [first, ...rest] = moreNumbers; // rest: зібрали залишок
console.log(first); // 0
console.log(rest); // [1, 2, 3, 4, 5]Spread: розпакування масивів
Spread використовується для розпакування (розкриття) масиву чи об'єкта на окремі елементи.
const numbers = [1, 2, 3];
const moreNumbers = [0, ...numbers, 4, 5];
console.log(moreNumbers); // [0, 1, 2, 3, 4, 5]Тут ...numbers «розкрив» масив [1, 2, 3] усередину нового масиву.
Копіювання масивів:
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false (новий масив)Це поверхнева копія (shallow copy): новим є лише зовнішній масив.
Об'єднання масивів:
const a = [1, 2];
const b = [3, 4];
const c = [...a, ...b];
console.log(c); // [1, 2, 3, 4]Spread у викликах функцій
У виклику функції spread перетворює масив на список окремих аргументів.
function sum(a, b, c) {
return a + b + c;
}
const nums = [1, 2, 3];
console.log(sum(...nums)); // 6
...numsперетворює[1, 2, 3]наsum(1, 2, 3).
Spread з об'єктами
Той самий синтаксис копіює власні перелічувані властивості з одного об'єкта в інший.
const user = { name: 'Alex', age: 25 };
const newUser = { ...user, city: 'Paris' };
console.log(newUser); // { name: 'Alex', age: 25, city: 'Paris' }Якщо ключі збігаються, пізніше значення перезаписує раніше:
const merged = { a: 1, ...{ a: 2, b: 3 } };
console.log(merged); // { a: 2, b: 3 }Rest: збирання залишку
Rest працює у зворотний бік: він не розпаковує, а збирає елементи, що залишилися, у масив або об'єкт.
У масивах:
const [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]В об'єктах:
const user = { name: 'Alex', age: 25, city: 'Paris' };
const { name, ...info } = user;
console.log(name); // Alex
console.log(info); // { age: 25, city: 'Paris' }У функціях:
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4)); // 10...numbers збирає всі аргументи функції у справжній масив, на відміну від псевдомасиву arguments.
Spread проти rest і що це замінило
| Особливість | Spread | Rest |
|---|---|---|
| Що робить | Розпаковує | Збирає |
| Де використовується | У виклику функції, при створенні масиву або об'єкта | В оголошенні функції або в деструктуризації |
| Приклад | [...arr], {...obj}, fn(...args) | function fn(...args), [a, ...rest] |
Приклади «до і після»:
| До ES6 | З ES6 |
|---|---|
arr1.concat(arr2) | [...arr1, ...arr2] |
Object.assign({}, obj1, obj2) | {...obj1, ...obj2} |
Array.prototype.slice.call(arguments) | (...args) => {} |
Підсумкова таблиця поведінки:
| Контекст | Поведінка ... | Приклад |
|---|---|---|
| Spread (розпакування) | Розгортає масив або об'єкт | [...arr], {...obj}, sum(...nums) |
| Rest (залишок) | Збирає залишок елементів | [a, ...rest], function fn(...args) |
Типові помилки
-
Вважати spread глибоким копіюванням. Spread і rest поверхневі: вкладені об'єкти та масиви копіюються за посиланням, тож зміна вкладеного поля в копії видно в оригіналі.
javascriptconst obj = { nested: { a: 1 } }; const copy = { ...obj }; copy.nested.a = 2; console.log(obj.nested.a); // 2, а не 1Для глибокої копії потрібен
structuredClone(obj)або ручний обхід структури. -
Використовувати
...без контексту деструктуризації. Записconst ...rest = arr;це синтаксична помилка; правильноconst [a, ...rest] = arr;. -
Ставити rest не останнім.
function fn(...args, last) {}не компілюється: rest-параметр може бути лише останнім, бо він забирає все, що лишилося. -
Забувати про порядок ключів у spread об'єктів.
{ ...defaults, ...options }і{ ...options, ...defaults }дають різний результат, бо перемагає той об'єкт, що стоїть правіше. -
Плутати
...argsзarguments.argumentsце псевдомасив, якого немає у стрілкових функціях; rest-параметр завжди дає справжній масив із методамиmap,filter,reduce.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.