← Назад к вопросам
Чем отличается 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 = остаток (оставшееся): собираешь оставшееся в коробку