Spread и Rest
Spread (
...) раскладывает массив/объект на элементы. Rest (...) собирает оставшиеся элементы в массив/объект.
Зачем нужно
Spread упрощает копирование, слияние и передачу данных. Rest позволяет функциям принимать произвольное количество аргументов.
Где используется
- Копирование и слияние массивов/объектов
- Передача аргументов в функции
- Вариативные функции (variadic)
- Иммутабельные обновления (React state)
Предпосылки
Массивы, Объекты, Деструктуризация
Spread в массивах
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// Объединение массивов
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Копия массива (поверхностная)
const copy = [...arr1]; // [1, 2, 3]
// Добавление элементов
const withStart = [0, ...arr1]; // [0, 1, 2, 3]
const withEnd = [...arr1, 4]; // [1, 2, 3, 4]
const withMiddle = [0, ...arr1, 4]; // [0, 1, 2, 3, 4]
// Из строки в массив
const chars = [..."Привет"]; // ['П', 'р', 'и', 'в', 'е', 'т']
// Из Set в массив (удаление дубликатов)
const unique = [...new Set([1, 2, 2, 3, 3])]; // [1, 2, 3]
// Из NodeList в массив
const divs = [...document.querySelectorAll('div')];
Spread в объектах
const defaults = { theme: 'light', lang: 'ru', debug: false };
const userPrefs = { theme: 'dark', fontSize: 16 };
// Слияние объектов (последний побеждает)
const config = { ...defaults, ...userPrefs };
// { theme: 'dark', lang: 'ru', debug: false, fontSize: 16 }
// Копия объекта (поверхностная)
const copy = { ...defaults };
// Добавление/перезапись свойств
const updated = { ...defaults, debug: true, version: 2 };
// Иммутабельное обновление (React-стиль)
const state = { count: 0, name: 'test' };
const newState = { ...state, count: state.count + 1 };
// { count: 1, name: 'test' } — state не изменён
Spread в вызовах функций
const numbers = [5, 2, 8, 1, 9];
// Math.max / Math.min
Math.max(...numbers); // 9
Math.min(...numbers); // 1
// Вместо apply
function sum(a, b, c) {
return a + b + c;
}
const args = [1, 2, 3];
sum(...args); // 6
// До ES6: sum.apply(null, args)
// console.log
console.log(...['a', 'b', 'c']); // a b c
Rest в параметрах функции
// Собирает «остаток» аргументов в массив
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
// Первый параметр отдельно, остальные — rest
function log(level, ...messages) {
console.log(`[${level}]`, ...messages);
}
log('ERROR', 'Файл не найден', 'path/to/file');
// [ERROR] Файл не найден path/to/file
// Rest ВСЕГДА последний параметр
// function bad(...rest, last) {} // SyntaxError!
Rest в деструктуризации
// Массивы
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]
// Объекты
const { name, ...otherProps } = { name: 'Алиса', age: 25, city: 'Москва' };
// name = 'Алиса', otherProps = { age: 25, city: 'Москва' }
// Практика: удаление свойства иммутабельно
const user = { id: 1, name: 'Алиса', password: 'secret' };
const { password, ...safeUser } = user;
// safeUser = { id: 1, name: 'Алиса' } — без пароля
Spread vs Rest — как отличить
// SPREAD — в правой части, раскладывает
const arr = [...otherArr]; // раскладывает otherArr
const obj = { ...otherObj }; // раскладывает otherObj
fn(...args); // раскладывает args при вызове
// REST — в левой части, собирает
const [a, ...rest] = arr; // собирает остаток в rest
const { x, ...others } = obj; // собирает остаток в others
function fn(...args) {} // собирает аргументы в args
Важно: поверхностная копия
const original = {
name: 'Алиса',
hobbies: ['чтение', 'музыка'],
address: { city: 'Москва' }
};
const copy = { ...original };
copy.name = 'Боб'; // OK — примитив скопирован
copy.hobbies.push('спорт'); // Мутирует оригинал!
copy.address.city = 'Питер'; // Мутирует оригинал!
console.log(original.hobbies); // ['чтение', 'музыка', 'спорт']
console.log(original.address.city); // 'Питер'
// Решение: structuredClone для глубокой копии
const deepCopy = structuredClone(original);
Частые ошибки
1. Spread не клонирует глубоко
// См. пример выше — вложенные объекты копируются по ссылке
2. Spread с null/undefined
const a = null;
// [...a]; // TypeError: null is not iterable
// Для объектов — OK
const obj = { ...null }; // {} — пустой объект
const obj2 = { ...undefined }; // {} — тоже OK
3. Порядок при слиянии
// Последний побеждает
const result = { a: 1, ...{ a: 2 } }; // { a: 2 }
const result2 = { ...{ a: 2 }, a: 1 }; // { a: 1 }
Практика
- Напиши функцию
mergeArrays(...arrays)— объединяет произвольное число массивов - Напиши функцию
omit(obj, ...keys())— возвращает объект без указанных ключей - Реализуй иммутабельное обновление вложенного объекта
- Используй spread для вызова функции с массивом аргументов
Связанные темы
Ресурсы
⚡ Источник: Выражаем себя через Rest и Spread · AsForJS
- 📅 2025-10-31 · YouTube
- Тезисы:
- Spread и Rest — это РАЗНЫЕ грамматические сущности, не один оператор. Их объединяет только токен
... - Spread в массиве/вызове функции использует
Symbol.iterator— работает только с iterables ([...new Set]OK,[...{}]— TypeError) - Spread в object literal
{...obj}НЕ использует итератор — копирует enumerable own properties (включая Symbol-ключи!) - Rest в параметре функции создаёт настоящий Array (в отличие от
arguments), который оптимизируется V8
- Spread и Rest — это РАЗНЫЕ грамматические сущности, не один оператор. Их объединяет только токен
- Цитата: «Spread не один оператор. Это три разных синтаксиса в трёх контекстах»