Урок 07

Spread и rest

Spread (...) раскрывает итерируемое. Rest (...) собирает в массив. Синтаксис один, но роль разная.

Spread в массивах

Копия

const copy = [...arr];

Поверхностная. Вложенные объекты — те же ссылки.

Объединение

const merged = [...a, ...b];
const withExtra = [0, ...arr, 10];

Замена методов

Старый способ:

Math.max.apply(null, [1, 2, 3]);

Новый:

Math.max(...[1, 2, 3]);

Преобразование

[...new Set([1, 2, 2, 3])]; // [1, 2, 3]
[...'abc'];                 // ['a', 'b', 'c']
[...map];                   // итерация Map

Spread в объектах

Копия

const copy = { ...user };

Слияние

const merged = { ...a, ...b };

При конфликте — последний выигрывает:

const merged = { ...{ a: 1, b: 2 }, ...{ b: 3 } };
// { a: 1, b: 3 }

Обновление

const updated = { ...user, name: 'Боб' };

Перезапись

const override = { name: 'Боб', ...user }; // name из user

Spread в вызовах функций

function add(a, b, c) {
    return a + b + c;
}

const args = [1, 2, 3];
add(...args);

Rest в параметрах

function sum(...numbers) {
    return numbers.reduce((a, b) => a + b, 0);
}

Несколько:

function log(level, ...messages) {
    console.log(`[${level}]`, ...messages);
}

Первый — level, остальные — messages.

Rest в деструктуризации

const [first, ...rest] = [1, 2, 3, 4];
const { name, ...others } = user;

Rest — всегда последний.

Разница

Spread — раскрывает:

const arr = [1, ...other, 2];

Rest — собирает:

function fn(...args) {}

Синтаксис ... одинаковый, но место разное:

  • В аргументах функции или в деструктуризации — rest
  • В выражении (массив, объект, вызов) — spread

Полезные паттерны

Удаление элемента без мутации

const without = items.filter((i) => i !== 'x');

Или через slice

const [, ...rest] = [1, 2, 3]; // без первого

Исключение полей

const { password, ...safeUser } = user;

Обновление вложенного

const updated = {
    ...user,
    address: {
        ...user.address,
        city: 'Питер',
    },
};

Массив объектов → объект

const indexed = Object.fromEntries(
    users.map((u) => [u.id, u])
);

Поверхностная копия

spread не глубокий:

const user = { name: 'Алиса', address: { city: 'Москва' } };
const copy = { ...user };

copy.address.city = 'Питер';
user.address.city; // 'Питер' — тот же объект

Глубокая:

const deep = structuredClone(user);

Spread с Map и Set

const map = new Map([['a', 1], ['b', 2]]);
[...map];        // [['a', 1], ['b', 2]]
[...map.keys()]; // ['a', 'b']
[...map.values()]; // [1, 2]

const set = new Set([1, 2, 3]);
[...set]; // [1, 2, 3]

Не работает с объектами

[...{ a: 1 }]; // ошибка: объект не итерируемый

Только с итерируемыми: массивы, строки, Map, Set.

Итоги

  • Spread раскрывает
  • Rest собирает
  • Spread для копий и слияний
  • Rest для вариативных аргументов
  • Поверхностная копия
  • structuredClone для глубокой