Зарегистрируйтесь, чтобы продолжить обучение

Оператор Rest (упаковка аргументов) JS: Функции

Давайте попробуем реализовать очень простую функцию, суммирующую числа. Для начала определим функцию sum(), принимающую на вход два числа и возвращающую их сумму:

const sum = (a, b) => a + b

sum(1, 2) // 3
sum(-3, 10) // 7

Пока всё просто и понятно. Сложности возникают при дополнительных требованиях: что, если захотим суммировать не два, а три числа? Или пять, или даже десять? Писать для обработки каждого случая отдельную функцию — очевидно плохой вариант:

const sumOfTwo = (a, b) => a + b
const sumOfTree = (a, b, c) => a + b + c
const sumOfTen = (a, b, c, d, e, f, g, h, i, j) => a + b + c + d + e + f + g + h + i + j // фух...
// const sumOfThousand = ???
// const sumOfMillion = ???

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

Можно заметить, что в стандартной библиотеке JavaScript существуют функции, которые могут принимать разное количество аргументов. Например, сигнатура функции Math.max() определяется так:

Math.max([value1[, value2[, ...]]])

Она говорит нам о том, что в Math.max() можно передать любое количество элементов и они не обязательны:

Math.max(10, 20) // 20
Math.max(10, 20, 30) // 30
Math.max(10, 20, 30, 40, 50) // 50
Math.max(-10, -20, -30) // -10

С точки зрения вызова — ничего необычного, просто разное число аргументов. А вот определение функции с переменным числом аргументов выглядит необычно:

const func = (...params) => {
  // params — это массив, содержащий все
  // переданные при вызове функции аргументы
  console.log(params)
}

func() // => []
func(9) // => [9]
func(9, 4) // => [9, 4]
func(9, 4, 1) // => [9, 4, 1]
func(9, 4, 1, -3) // => [9, 4, 1, -3]

Символ многоточия ... перед именем формального параметра в определении функции обозначает rest-оператор. Запись ...params в определении func() из примера выше означает буквально следующее: "все переданные при вызове функции аргументы поместить в массив params".

Если вовсе не передать аргументов, то массив params будет пустым:

func() // => []

В функцию можно передать любое количество аргументов — все они попадут в массив params:

func(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Аргументы могут быть любого типа — числа, строки, массивы и др.:

func(1, 2, 'hello', [3, 4, 5], true)
// => [1, 2, 'hello', [3, 4, 5 ], true]

Теперь у нас достаточно знаний, чтобы с помощью rest-оператора переписать нашу функцию sum() так, чтобы она умела суммировать любое количество чисел (а не только два числа, как сейчас):

const sum = (...numbers) => {
  // По умолчанию 0, так как сумма ничего это 0
  let result = 0
  for (const num of numbers) {
    result += num
  }
  return result
}

sum() // 0
sum(10) // 10
sum(10, 4) // 14
sum(8, 10, 4) // 22

В таком контексте массив можно считать как "необязательные аргументы", которые можно либо вовсе не передавать, либо передавать столько, сколько хочешь. А что, если мы захотим, чтобы функция имела два обыкновенных ("обязательных") именованных параметра, а остальные были необязательными и сохранялись в rest-массиве? Всё просто: при определении функции сначала указываем стандартные именованные формальные параметры (например, a и b) и в конце добавляем rest-массив:

const func = (a, b, ...params) => {
  // параметр 'a' содержит первый аргумент
  console.log(`a -> ${a}`)
  // параметр 'b' содержит второй аргумент
  console.log(`b -> ${b}`)
  // params содержит все остальные аргументы
  console.log(params)
}

func(9, 4)
// => a -> 9
// => b -> 4
// => []
func(9, 4, 1)
// => a -> 9
// => b -> 4
// => [1]
func(9, 4, 1, -3)
// => a -> 9
// => b -> 4
// => [1, -3]
func(9, 4, 1, -3, -5)
// => a -> 9
// => b -> 4
// => [1, -3, -5]

То же можно сделать и для одного аргумента:

const func = (a, ...params) => {
  // ...
}

и для трёх:

const func = (a, b, c, ...params) => {
  // ...
}

Эту идею можно продолжать и дальше, делая обязательными то количество аргументов, которое требуется. Единственное ограничение: rest-оператор может быть использован только для последнего параметра. То есть такой код синтаксически неверен:

const func = (...params, a) => {
  // ...
}

И такой тоже:

const func = (a, ...params, b) => {
  // ...
}

Именно поэтому оператор называется rest, то есть он организует хранение "остальных" ("оставшихся", последних) параметров.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff