Оператор ... в JavaScript используется в двух разных смыслах:
- spread - “развернуть” значения (копирование, объединение)
- rest - “собрать остаток” (деструктуризация, параметры функций)
Синтаксис один и тот же (...), но смысл зависит от места, где он применяется.
1) Spread: массивы (копия и объединение)
Копия массива
const a = [1, 2, 3];
const b = [...a];
b[0] = 99;
console.log(a); // [1, 2, 3]
console.log(b); // [99, 2, 3]
Объединение массивов
const a = [1, 2];
const b = [3, 4];
const merged = [...a, ...b];
console.log(merged); // [1, 2, 3, 4]
Вставка элементов в середину
const a = [1, 2, 5, 6];
const withMiddle = [ ...a.slice(0, 2), 3, 4, ...a.slice(2) ];
console.log(withMiddle); // [1, 2, 3, 4, 5, 6]
2) Spread: объекты (копия и объединение)
Копия объекта
const a = { x: 1, y: 2 };
const b = { ...a };
b.x = 99;
console.log(a); // { x: 1, y: 2 }
console.log(b); // { x: 99, y: 2 }
Объединение объектов
Если ключи совпадают, выигрывает тот, что справа.
const base = { theme: "light", lang: "en" };
const overrides = { theme: "dark" };
const settings = { ...base, ...overrides };
console.log(settings); // { theme: "dark", lang: "en" }
Добавление/замена свойства при копировании
const user = { id: 1, name: "Sam" };
const updated = { ...user, name: "Alex" };
console.log(updated); // { id: 1, name: "Alex" }
3) Spread в вызове функции
Spread “разворачивает” массив в список аргументов.
function sum(a, b, c) {
return a + b + c;
}
const nums = [1, 2, 3];
console.log(sum(...nums)); // 6
Это альтернатива ручной передаче nums[0], nums[1], nums[2].
4) Rest: массивы в деструктуризации
Rest собирает оставшиеся элементы в массив.
const nums = [10, 20, 30, 40];
const [first, second, ...rest] = nums;
console.log(first); // 10
console.log(second); // 20
console.log(rest); // [30, 40]
5) Rest: объекты в деструктуризации
Rest собирает оставшиеся свойства в новый объект.
const user = { id: 1, name: "Sam", age: 20 };
const { id, ...rest } = user;
console.log(id); // 1
console.log(rest); // { name: "Sam", age: 20 }
6) Rest parameters: сбор аргументов функции
Rest в параметрах функции собирает “хвост” аргументов в массив.
function sumAll(...nums) {
return nums.reduce((acc, n) => acc + n, 0);
}
console.log(sumAll(1, 2, 3)); // 6
console.log(sumAll(10, 20, 30)); // 60
Можно комбинировать обычные параметры и rest:
function log(level, ...messages) {
console.log(level, messages);
}
log("info", "a", "b", "c"); // "info" ["a", "b", "c"]
Rest должен быть последним параметром.
7) Поверхностная копия: важный нюанс
Spread делает поверхностную копию. Если внутри лежат объекты, копируются ссылки.
const a = { user: { name: "Sam" } };
const b = { ...a };
b.user.name = "Alex";
console.log(a.user.name); // "Alex"
console.log(b.user.name); // "Alex"
Копия сделана только на верхнем уровне.
Частые ошибки
1) Путаница spread и rest
Spread “разворачивает”, rest “собирает”.
const nums = [1, 2, 3];
function f(a, b, c) {
return a + b + c;
}
console.log(f(...nums)); // spread
function g(...args) {
return args.length;
}
console.log(g(1, 2, 3)); // rest
2) Rest не может быть не последним параметром
try {
eval("function bad(...args, last) { return last; }");
} catch (e) {
console.log(e.name); // "SyntaxError"
}
Что стоит запомнить
...это либо spread, либо rest - зависит от контекста.- Spread: копировать и объединять массивы/объекты, передавать массив как аргументы.
- Rest: собирать остаток в деструктуризации и собирать аргументы функции.
- Spread делает поверхностную копию, вложенные объекты остаются общими по ссылке.