Что такое «параметры покоя»? Зачем нам вообще использовать синтаксис распространения?

Мы начнем с остальных параметров ....

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

Например:

function mult(f, g){
   return f * g;
}
console.log(mult(1, 2, 3, 4, 5, 6))

Не будет ошибки ⛔ из-за «лишних» аргументов. Но, конечно, в результате будут учитываться только первые два.

Остальные параметры можно включить в определение функции, используя три точки ..., за которыми следует имя массива, который будет их содержать. Точки буквально означают «собрать оставшиеся параметры в массив».

Например, чтобы собрать все аргументы в массив args:

function multAll(...args) { // args is the name for the array 
let product = 0;
 for (let arg of args) 
   product *= arg; 
return product; 
} 
console.log(multAll(1) ); // 1 
console.log(multAll(1, 2) ); // 2 
console.log(multAll(1, 2, 3) ); // 6

Мы можем получить первые параметры как переменные и собрать только остальные.

Здесь первые два аргумента переходят в переменные, а остальные — в titles массив:

function showName(firstName, lastName, ...titles) { 
  console.log(firstName + ' ' + lastName); // Dante Alighieri   
  // The rest go into titles array   
  // i.e. titles = ["Consul", "Imperator"]  
  console.log(titles[0] ); // Poet
  console.log(titles[1] ); // Philosopher
  console.log(titles.length ); // 2 
}
showName("Dante", "Alighieri", "Poet", "Philosopher");

Остальные параметры должны быть в конце

Остальные параметры собирают все оставшиеся аргументы, поэтому следующее не имеет смысла и вызывает ошибку:

function g(a, ...rest, b){
  // Error
}

...rest всегда должен быть последним.

Далее мы переходим к синтаксису Spread.

Мы только что увидели, как получить массив из списка параметров 📝.

Но иногда нам нужно сделать ровно наоборот.

Например, есть встроенная функция Math.min(), которая возвращает наименьшее число из списка:

console.log(Math.min(3, 5, 1) ); // 1

Теперь предположим, что у нас есть массив [3, 5, 1]. Как мы называем Math.min()с этим?

Передача «как есть» не сработает, потому что Math.min() ожидает список числовых аргументов, а не один массив:

let arr = [3, 5, 1]; 
console.log(Math.min(arr) ); // NaN

И, конечно же, мы не можем вручную 🦽 перечислить элементы в коде Math.min(arr[0], arr[1], arr[2]), потому что мы можем быть не уверены, сколько их. По мере выполнения нашего кода их может быть много, а может и не быть вовсе. И это было бы некрасиво.

Распространяйте синтаксис на помощь! Это похоже на остальные параметры, также использующие ..., но делает совсем наоборот.

Когда ...arr используется в вызове функции, он «расширяет» повторяемый объект arr в списке аргументов.

Для Math.min():

let arr = [3, 5, 1];  
console.log(Math.min(...arr)); // 1 (Spread turns array into a list of arguments)

Мы также можем передать несколько итераций следующим образом:

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
console.log(Math.min(...arr1, ...arr2) ); // -8

Мы даже можем комбинировать синтаксис распространения с нормальными значениями:

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -18, 1];
console.log(Math.min(1, ...arr1, 2, ...arr2, 25) ); // -18

Кроме того, синтаксис распространения может использоваться для объединения массивов:

let arr = [4, 3, 2]; 
let arr2 = [5, 8, 25]; 
let merged = [0, ...arr, 2, ...arr2]; 
console.log(merged); // 0, 4, 3, 2, 2, 5, 8, 25 (0, then arr, then 2, then arr2)

В приведенных выше примерах мы использовали массив для демонстрации синтаксиса распространения, но подойдет любой итерируемый объект.

Например, здесь мы используем синтаксис расширения, чтобы превратить строку в массив символов:

let str = "Hello";
console.log([...str] ); // 'H', 'e', 'l', 'l', 'o'

Синтаксис расширения использует итераторы для сбора элементов так же, как это делает for...of.

Итак, для строки for...of возвращает символы, а ...str становится 'H', 'e', 'l', 'l', 'o'. Список символов передается инициализатору массива [...str].

Для этой конкретной задачи мы также могли бы использовать Array.from, потому что он преобразует итерируемый объект (например, строку) в массив:

let str = "Hello";  // Array.from() converts an iterable into an array console.log(Array.from(str) ); // H, e, l, l, o

Результат такой же, как [...str].

Но есть тонкая разница между Array.from(obj) и [...obj]:

  • Array.from() работает как с массивами, так и с итерируемыми объектами.
  • Синтаксис распространения работает только с итерируемыми объектами.

Таким образом, для задачи преобразования чего-либо в массив Array.from() имеет тенденцию быть более универсальным.

Заключение

Когда мы видим ... в коде, это либо остальные параметры, либо синтаксис расширения.

Есть простой способ отличить их 😵:

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

Используйте шаблоны 🔭:

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