Слышали ли вы когда-нибудь, как разработчики говорили о spread- или rest-операторах, или же просто видели ... (троеточие) в коде JS и задавались вопросом - что это за штука? Это как раз и есть один из этих операторов, о которых и поговорим.

Синтаксис оператора rest и spread выглядит одинаково, однако они полярно противоположны друг другу с точки зрения функциональности.

Рассмотрим их поочереди.

rest-оператор

Оператор rest используется для получения неопределенного количества аргументов в функции и объединения их в массив. Синтаксически он имеет троеточие в начале.

function foo(...bar){
  return bar;
};

console.log(foo(1,2,3,4));  // => [ 1, 2, 3, 4 ]
console.log(foo(1,2));  // => [ 1, 2 ]

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

foo(1,2,3,4);

function foo(a,b,...bar){
  console.log(a);  // => 1
  console.log(b);  // => 2
  console.log(bar);  // => [ 3, 4 ]
};

Что мы сделали выше - это сказали функции, что собираемся передать в нее a и b, а все, что будет передано после этого, сгруппируется в массив.

Сразу возникает вопрос - что произойдет, если не передать значение для rest-оператора?

foo(1,2);

function foo(a,b,...bar){
  console.log(a);  // => 1
  console.log(b);  // => 2
  console.log(bar);  // => []
};

А что произойдет, если я сдвину последний параметр на первую или вторую позицию?

function foo(a,...bar,b){  // SyntaxError: Rest parameter must be last formal parameter
  console.log(a);
  console.log(b);
  console.log(bar);
};

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

Теперь, когда мы знаем, что такое оператор rest, рассмотрим один из вариантов его использования.

Предположим, вам сказали создать функцию, которая принимает два значения и возвращает их сумму. Легко? Звучит как да. Однако давайте теперь расширим ее. И теперь вам нужно создать ту же функцию, но приниматься может любое возможное количество параметров, и ваша функция должна возвращать сумму всех аргументов.

Зная оператор rest, вам не следует беспокоиться о сложности выполнения данной задачи.

function total(...values){
  let sum = 0;
  for(let i = 0; i< values.length; i++){
    sum += values[i]
  }
  return sum;
};

console.log(total(1,2,3,4,5));  // => 15

spread-оператор

Выше упоминалось, что и rest и spread имеют одинаковый синтаксис, и все же они противоположны друг другу. Там, где оператор rest принимает несколько значений и группирует их, оператор spread разгруппировывает их.

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

Как видим, оператор spread распаковывает значение массива, и таким же образом работает с объектом.

Spread-оператор можно использовать во многих различных задачах, таких как:

  • Копирование массива
  • Добавление данных в state в React
  • Объединение массивов
  • Использование математических функций
  • Преобразование NodeList в массив
  • Использование массива в качестве аргументов
  • Добавление элемента в список
  • Объединение объектов

Вот как spread-оператор работает с объектами:

const obj = {
  hello : "world",
  foo : "bar"
};

console.log(obj);  // => { hello: 'world', foo: 'bar' }
console.log(...obj);  // TypeError: Found non-callable @@iterator
console.log({...obj});  // => { hello: 'world', foo: 'bar' }

Также можно создать не только клон объекта, а и обновить его значение:

// Создание клона массива и обновление его
const arr = [1,2,3];
console.log([...arr, 4,5]);  // => [ 1, 2, 3, 4, 5 ]

// Создание клона объекта и обновление его
const obj = {
  hello : "world",
  foo : "bar"
};
console.log({...obj, foo : "zen"});  // => { hello: 'world', foo: 'zen' }