Операции spread и rest

1. Введение

В современном JavaScript появилась новая операция для работы с итерируемыми объектами такими как строка, массив или объект. Ее функционал и название, в отличии от синтаксиса, зависит от места применения.

2. Операция spread

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

Распыление можно использовать всего в 3-х ситуациях:

  • Во время вызова функции, для передачи массива элементов отдельными аргументами

  • В литерале массива, при создании нового массив

  • В литерале объекта, при создании нового объекта

2.1. Распыление аргументов

Представим ситуацию когда у нас есть массив температур за какой-то период и необходимо программно найти самую маленькую температуру в массиве используя метод Math.min(), который ожидает несколько аргументов, а не массив.

Если передать просто массив, получим NaN, потому что Math.min() не умеет работать с массивом.

const temperatures = [18, 14, 12, 21, 17, 29];

const min = Math.min(temperatures); // NaN
Copy

В данном случае нам поможет операция распыления, чтобы передать элементы массива отдельными аргументами при вызове функции.

const temperatures = [18, 14, 12, 21, 17, 29];

const min = Math.min(...temperatures); // 12
Copy

То есть запись Math.min(...[18, 14, 12, 21, 17, 29]) аналогична Math.min(18, 14, 12, 21, 17, 29), операция spread распылила массив элементов в аргументы функции.

2.2. Распыление массивов

При работе с массивами можно скопировать элементы одного массива в другой. Массив copyOfHouses будет независимой копией массива houses и, соответственно, изменения в любом из них не отобразятся в другом.

const houses = ['Arryn', 'Frey', 'Greyjoy', 'Stark', 'Lannister', 'Tyrell'];
const copyOfHouses = [...houses];

console.log(houses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell']
console.log(copyOfHouses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell']
console.log(houses === copyOfHouses); // false - разные ссылки
Copy

То же самое можно было сделать используя метод slice(), операция spread просто позволяет записывать это более кратко, особенно когда распылений несколько.

const houses = ['Arryn', 'Frey', 'Greyjoy', 'Stark', 'Lannister', 'Tyrell'];
const slicedCopyOfHouses = houses.slice();

console.log(houses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell']
console.log(slicedCopyOfHouses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell']
Copy

При этом можно добавлять и удалять элементы при создании нового массива.

const houses = ['Arryn', 'Frey', 'Greyjoy', 'Stark', 'Lannister', 'Tyrell'];
const completeHouses = [...houses, 'Targaryen'];

console.log(houses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell']
console.log(completeHouses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell','Targaryen']
Copy

Можно очень удобно соединять несколько массивов в один новый. Порядок распыления исходных массивов влияет на порядок элементов в результирующем массиве.

const firstBatch = ['Arryn', 'Frey', 'Greyjoy'];
const secondBatch = ['Stark', 'Lannister', 'Tyrell'];

const houses = [...firstBatch, ...secondBatch];

console.log(houses); // ['Arryn','Frey','Greyjoy','Stark','Lannister','Tyrell']
Copy

Можно использовать классические методы массива вроде slice() в комбинации с операцией spread. Все, кто смотрел сериал Game of Thrones знают, что никто, кроме дома Ланистеров не платит по долгам. Сделаем массив содержащий все дома, кроме Ланистеров.

const houses = ['Arryn', 'Frey', 'Greyjoy', 'Stark', 'Lannister', 'Tyrell'];
const housesInDebt = [...houses.slice(0, 4), ...houses.slice(5)];

console.log(housesInDebt); // ['Arryn','Frey','Greyjoy','Stark','Tyrell']
Copy

Можно обновлять элементы не изменяя оригинальный массив, а создавая новый с обновленными значениями.

const houses = ['Arryn', 'Frey', 'Greyjoy', 'Stark', 'Lannister', 'Tyrell'];

// Вертикальная запись удобнее для чтения
const updatedHouses = [
  ...houses.slice(0, 1),
  'Frey of the Crossing',
  ...houses.slice(2),
];

console.log(updatedHouses);
// ['Arryn', 'Frey of the Crossing', 'Greyjoy', 'Stark', 'Lannister', 'Tyrell']
Copy

2.3. Распыление объектов

Для объектов, в будущих стандартах языка, будет так же утверждена операция spread. А в современных браузерах, и используя такие инструменты как Babel, его можно применять уже сейчас. Он позволяет распылять свойства одного объекта в другой.

Распыление можно использовать как замену Object.assign(), то есть когда необходимо создать новый объект или обновить уже существующий по результату слияния нескольких других объектов.

const a = { x: 1, y: 2 };
const b = { x: 0, z: 3 };

const c = Object.assign({}, a, b);
console.log(c); // {x: 0, y: 2, z: 3}

// То же самое используя операцию spread
const d = { ...a, ...b };

console.log(d); // {x: 0, y: 2, z: 3}
Copy

Во время слияния можно добавлять свойства в произвольное место. Но следует помнить, что свойства распыляемого объекта могут перезаписать значение существующего свойства, если имена ключей совпадают, а распыление происходит после указания свойства.

const a = { x: 1, y: 2 };
const b = { x: 0, z: 3 };

const c = { x: 5, j: 10, ...a, z: 15, ...b };

console.log(c); // {x: 0, j: 10, y: 2, z: 3}
Copy

Создадим химеру :)

const lion = { hasTail: true, legs: 4 };
const eagle = { canFly: true };

const chimera = { ...lion, ...eagle };

console.log(chimera); // {hasTail: true, legs: 4, canFly: true}
Copy

3. Операция rest

... - rest, сбор. Позволяет скопировать группу элементов коллекции в новую коллекцию, не изменяя оригинальную. При этом, во время сбора, если элемент коллекции примитив - создается копия элемента, в случае сложного типа создается ссылка на оригинальный элемент.

Используется в подписи функции для сбора аргументов или при деструктуризации, для хранения остатка элементов.

Деструктуризация массивов и объектов описана в следующем разделе, а функции рассмотрим сейчас.

Есть функция add, которая складывает произвольное кол-во аргументов. Получить доступ ко всем аргументам можно используя специальную переменную arguments, но в стрелочных функциях ее нет, а в обычных функциях - она псевдомассив. Используя операцию rest можно собрать все аргументы в массив прямо в подписи функции.

const add = function (...args) {
  console.log(args); // массив всех аргументов
};

add(1, 2, 3);
add(1, 2, 3, 4, 5);
Copy

Теперь предположим, что функция add не просто считает сумму аргументов, а еще получает первым аргументом число, которое необходимо прибавлять к каждому последующему аргументу при вычислении суммы.

Используя rest можно взять только ту часть аргументов, которая необходима, объявив параметры до rest. Все аргументы, для которых будут объявлены параметры, передадут им свои значения, все остальные будут помещены в массив args.

const add = function (value, ...args) {
  console.log(value); // первый аргумент
  console.log(args); // массив всех остальных аргументов
};

add(10, 1, 2, 3);
add(15, 1, 2, 3, 4, 5);
Copy

4. Дополнительные материалы

Last updated