Learn JavaScript

spread - оператор ... для раскрытия значений в JavaScript

Spread - это использование ... для раскрытия значений.

Если говорить просто, spread берёт одно значение и “раскладывает” его на отдельные элементы или свойства.

Именно поэтому его часто используют при вызове функций, копировании массивов, объединении массивов и объектов.

Синтаксис

Базовая идея выглядит так:

...value

Но сам по себе spread не используется где угодно. Обычно он встречается в трёх местах:

  • при вызове функции
  • в массивах
  • в объектах

Что делает spread

Spread раскрывает содержимое значения наружу.

Например, массив:

const numbers = [1, 2, 3];

console.log(...numbers); // 1 2 3

Здесь массив не передаётся как один объект целиком. Его элементы раскрываются по отдельности.

Spread при вызове функции

Это один из самых частых сценариев.

const numbers = [5, 10, 20];

console.log(Math.max(...numbers)); // 20

Без spread функция получила бы один массив вместо отдельных аргументов.

const numbers = [5, 10, 20];

// Math.max(numbers); // NaN

Math.max() ждёт список чисел, а не массив. Spread как раз помогает передать массив как набор аргументов.

Spread в массивах

Внутри массива spread раскрывает элементы другого массива или другого итерируемого значения.

const first = [1, 2];
const second = [3, 4];

const result = [...first, ...second];

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

Это очень удобно для объединения массивов.

Копия массива через spread

Spread часто используют для поверхностного копирования массива.

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

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

Получается новый массив, но элементы внутри пока те же самые по значению или по ссылке.

Добавление элементов в новый массив

Spread удобно комбинируется с обычными значениями.

const numbers = [2, 3];
const result = [1, ...numbers, 4];

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

Это читается проще, чем ручная сборка через цикл.

Spread работает не только с массивами

В массивах и при вызове функции spread хорошо работает с итерируемыми значениями, например со строками.

const chars = [..."JS"];

console.log(chars); // ["J", "S"]

Можно использовать и Set:

const uniqueNumbers = new Set([1, 2, 2, 3]);

console.log([...uniqueNumbers]); // [1, 2, 3]

Spread в объектах

В объектах spread копирует собственные перечисляемые свойства.

const user = { name: "Анна", age: 25 };
const copy = { ...user };

console.log(copy); // { name: "Анна", age: 25 }

Это удобный способ быстро создать новый объект на основе существующего.

Объединение объектов

Spread особенно часто используют для объединения объектов.

const user = { name: "Анна" };
const extra = { role: "admin" };

const result = { ...user, ...extra };

console.log(result); // { name: "Анна", role: "admin" }

Такой код очень часто встречается в современном JavaScript и особенно в React-коде.

Если свойства повторяются

Если одно и то же свойство встречается несколько раз, побеждает последнее значение.

const user = { name: "Анна", role: "user" };
const updated = { ...user, role: "admin" };

console.log(updated); // { name: "Анна", role: "admin" }

Это полезно для обновления отдельных полей без мутации исходного объекта.

Поверхностная копия, а не глубокая

Это очень важный момент.

Spread делает только поверхностную копию.

const user = {
  name: "Анна",
  address: { city: "Москва" },
};

const copy = { ...user };

copy.address.city = "Казань";

console.log(user.address.city); // "Казань"

Вложенный объект address не был скопирован глубоко. Скопировалась только верхняя структура.

То же самое касается вложенных массивов и объектов внутри массива.

Чем spread отличается от rest

И spread, и rest используют ..., поэтому их часто путают.

Но идея противоположная:

  • spread раскрывает одно значение в несколько
  • rest собирает несколько значений в одно

Spread:

const numbers = [1, 2, 3];

console.log(...numbers); // 1 2 3

Rest:

function sum(...numbers) {
  console.log(numbers); // [1, 2, 3]
}

Если ... стоит в параметрах функции, это обычно rest.

Если ... стоит в вызове функции, массиве или объекте, это spread.

Когда это полезно

Spread особенно удобен, когда:

  • нужно передать массив как набор аргументов
  • нужно объединить массивы
  • нужно сделать копию массива или объекта
  • нужно обновить объект без изменения исходного значения
  • нужно добавить элементы в новый массив или свойства в новый объект

Пример:

function greet(name, city) {
  console.log(`Привет, ${name} из ${city}!`);
}

const userData = ["Анна", "Москва"];

greet(...userData);

Частые ошибки

Путать spread и rest

function sum(...numbers) {
  return numbers;
}

Здесь ...numbers в параметрах - это rest, а не spread.

Думать, что spread делает глубокую копию

const user = {
  profile: { age: 25 },
};

const copy = { ...user };

Вложенные объекты и массивы всё ещё могут ссылаться на те же данные.

Использовать spread там, где значение не раскрывается таким образом

Например, обычный объект нельзя просто так раскрыть в аргументы функции или в массив:

const user = { name: "Анна" };

// console.log(...user); // TypeError
// const arr = [...user]; // TypeError

Обычный объект не является итерируемым значением.

Но в объекте spread с ним работает нормально:

const copy = { ...user };

Забывать, что последнее свойство перезаписывает предыдущее

const result = { name: "Анна", ...{ name: "Игорь" } };

console.log(result); // { name: "Игорь" }

Порядок здесь важен.

Ожидать, что spread изменит исходный массив или объект

const numbers = [1, 2];
const copy = [...numbers];

copy.push(3);

console.log(numbers); // [1, 2]

Spread создаёт новое значение верхнего уровня, а не меняет исходное.

Быстрая памятка

  • spread использует ... для раскрытия значений
  • при вызове функции spread превращает массив в набор аргументов
  • в массивах spread помогает копировать и объединять элементы
  • в объектах spread помогает копировать и объединять свойства
  • spread делает только поверхностную копию
  • spread и rest используют одинаковый синтаксис, но делают противоположные вещи

Коротко

Spread в JavaScript позволяет раскрывать массивы, строки, итерируемые значения и свойства объектов через .... Главное, что стоит запомнить: он очень удобен для вызова функций, копирования и объединения данных, но не делает глубокую копию и не должен путаться с rest-параметрами.