← Назад к вопросам

Чем отличается spread-оператор от rest-оператора?

1.6 Junior🔥 301 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Spread vs Rest оператор: ...синтаксис

Это один из самых практических и часто путаемых синтаксисов в современном JavaScript. После 10+ лет разработки скажу: правильное понимание разницы критично для чистого и эффективного кода.

Главная разница

ОператорНаправлениеЧто делаетГде используется
Spread (...)РаспаковкаРазворачивает элементыПри вызове функции, в литералах массива/объекта
Rest (...)УпаковкаСобирает элементыВ параметрах функции, деструктуризация
const arr = [1, 2, 3];

// Spread — РАСПАКОВКА
const newArr = [...arr]; // [1, 2, 3]
console.log(...arr); // 1, 2, 3

// Rest — УПАКОВКА
function sum(...numbers) { // numbers = [1, 2, 3]
  return numbers.reduce((a, b) => a + b);
}
console.log(sum(1, 2, 3)); // 6

SPREAD оператор (распаковка)

Spread разворачивает итерируемый объект (массив, строку) в отдельные элементы.

1. Копирование массива

const original = [1, 2, 3];
const copy = [...original];

copy.push(4);

console.log(original); // [1, 2, 3]
console.log(copy);     // [1, 2, 3, 4]

// Это поверхностное копирование
const nestedOriginal = [{ id: 1 }, { id: 2 }];
const nestedCopy = [...nestedOriginal];
nestedCopy[0].id = 999; // меняет и оригинал!
console.log(nestedOriginal[0].id); // 999 ✓ изменился

2. Объединение массивов

const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5, 6];

// ✅ Современный способ
const combined = [...arr1, ...arr2, ...arr3];
console.log(combined); // [1, 2, 3, 4, 5, 6]

// ❌ Старый способ
const combined = arr1.concat(arr2).concat(arr3);

// Можно добавлять элементы
const withExtra = [0, ...arr1, 1.5, ...arr2, 7];
console.log(withExtra); // [0, 1, 2, 1.5, 3, 4, 7]

3. Копирование объекта

const user = { name: "John", email: "john@example.com" };
const updated = { ...user, email: "newemail@example.com" };

console.log(user);    // { name: "John", email: "john@example.com" }
console.log(updated); // { name: "John", email: "newemail@example.com" }

// Merge объектов
const profile = { bio: "Developer", location: "NYC" };
const merged = { ...user, ...profile };
console.log(merged);
// { name: "John", email: "john@example.com", bio: "Developer", location: "NYC" }

4. Передача параметров функции

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

const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6

// Эквивалентно
console.log(sum(1, 2, 3)); // 6

// Это очень полезно для функций с неизвестным количеством аргументов
const args = [true, "Hello", 100];
API.request(...args); // передаём сразу

5. Строка

const str = "hello";
const chars = [...str];
console.log(chars); // ["h", "e", "l", "l", "o"]

// Это удобнее чем
const chars2 = str.split("");

// В объекте для перебора
for (const char of [...str]) {
  console.log(char);
}

6. Array-like объекты

const listItems = document.querySelectorAll("li");

// ✅ Spread преобразует в настоящий массив
const items = [...listItems];
console.log(items.map); // есть метод map

// ❌ Без spread это array-like, без методов массива
const items2 = listItems;
console.log(items2.map); // undefined

REST оператор (упаковка)

Rest собирает оставшиеся элементы в массив.

1. Параметры функции

// ✅ Rest параметр
function sum(...numbers) {
  console.log(numbers); // массив всех аргументов
  return numbers.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum()); // 0

// Rest должен быть последним параметром
function greet(greeting, ...names) {
  console.log(greeting, names);
}
greet("Hello", "John", "Jane", "Bob"); // "Hello" ["John", "Jane", "Bob"]

2. Деструктуризация массива

const arr = [1, 2, 3, 4, 5];

const [first, second, ...rest] = arr;
console.log(first);  // 1
console.log(second); // 2
console.log(rest);   // [3, 4, 5]

// Пропускание элементов
const [a, , , ...rest2] = arr;
console.log(a);     // 1
console.log(rest2); // [4, 5]

3. Деструктуризация объекта

const user = {
  id: 1,
  name: "John",
  email: "john@example.com",
  role: "admin",
  status: "active"
};

// Выбираем нужные свойства, остальное в rest
const { id, name, ...rest } = user;
console.log(id);   // 1
console.log(name); // "John"
console.log(rest); // { email: "john@example.com", role: "admin", status: "active" }

// Очень полезно при отделении пропсов
function Component({ id, name, ...props }) {
  // id и name получены, остальные пропсы в props
  return <div {...props}></div>;
}

4. Исключение элементов

const [, , ...excludeFirst2] = [1, 2, 3, 4, 5];
console.log(excludeFirst2); // [3, 4, 5]

Практические примеры

React компоненты

// Spread для пропсов
function Button(props) {
  return <button {...props}>Click</button>;
}

// Rest для выделения специальных пропсов
function Card({ variant, size, ...props }) {
  // variant и size обработаны компонентом
  // остальные пропсы передаются дальше
  return <div className={`card-${variant} card-${size}`} {...props} />;
}

const CardComponent = (
  <Card
    variant="primary"
    size="lg"
    className="custom"
    onClick={handleClick}
    id="myCard"
  />
);

Функции утилит

// Функция берёт первый элемент и остальное
function process(first, ...rest) {
  console.log("First:", first);
  console.log("Others:", rest);
}
process(1, 2, 3, 4); // First: 1, Others: [2, 3, 4]

// Функция добавляет к массиву
function addDefaults(defaults, ...userOptions) {
  return { ...defaults, ...Object.assign({}, ...userOptions) };
}

// Функция фильтрует
function getAllExcept(excluded, ...items) {
  return items.filter(item => item !== excluded);
}
console.log(getAllExcept(2, 1, 2, 3, 4)); // [1, 3, 4]

Клонирование и слияние

// Deep merge с spread (только первый уровень)
const defaults = { theme: "light", timeout: 5000, retry: 3 };
const userConfig = { theme: "dark", retry: 5 };
const config = { ...defaults, ...userConfig };
console.log(config); // { theme: "dark", timeout: 5000, retry: 5 }

// Удаление свойства
const { password, ...safeUser } = user;
// password удалён из safeUser

Производительность

const arr = new Array(1000000).fill(0);

// Spread может быть медленнее для больших массивов
console.time("spread");
const copy1 = [...arr];
console.timeEnd("spread"); // ~10ms

console.time("slice");
const copy2 = arr.slice();
console.timeEnd("slice"); // ~5ms

// Для обычных случаев разница незначительна
const small = [1, 2, 3];
const copy3 = [...small]; // никакого заметного замедления

Распространённые ошибки

// ❌ Rest не может быть первым
function bad(...rest, last) {} // SyntaxError

// ❌ Spread в функции — это не rest
function func(a, b) {
  return a + b;
}
func(...[1, 2]); // 3 — это spread, не rest

// ✅ Правильно
function correct(...args) { // это rest
  return args.reduce((a, b) => a + b);
}
correct(1, 2, 3, 4); // 10

Заключение

Spread (распаковка):

  • Разворачивает итерируемые объекты
  • Используется при передаче аргументов, в литералах
  • sum(...arr), [...arr1, ...arr2], { ...obj }

Rest (упаковка):

  • Собирает оставшиеся элементы
  • Используется в параметрах и деструктуризации
  • function(...items), const [a, ...rest] = arr

Практический способ запомнить:

  • Spread = распаковка (распространение): берёшь коробку, вынимаешь
  • Rest = остаток (оставшееся): собираешь оставшееся в коробку
Чем отличается spread-оператор от rest-оператора? | PrepBro