Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос нашим менторам. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Оператор Rest (упаковка аргументов)

Давайте попробуем реализовать очень простую функцию, суммирующую числа. Для начала определим функцию 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 sumoOfThousand = ???
// 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]

https://repl.it/@hexlet/js-functions-rest-operator-rest-parameters

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

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

func(); // => []

В функцию можно передать любое количество аргументов — все они попадут в rest-массив 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]

Основная сложность для понимания оператора ... состоит в том, что он выполняет различные действия в зависимости от того, где применяется. В определении функции он выполняет "упаковку" параметров, а при вызове — наоборот, "распаковку". Упаковке параметров посвящён этот урок и вы уже видели, как это делается. В следующем же уроке рассмотрим ... в роли распаковщика (spread-оператор).

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

const sum = (...numbers) => {
  let result = 0;
  for (let num of numbers) {
    result += num;
  }
  return result;
};

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

https://repl.it/@hexlet/js-functions-rest-operator-sum

Наша реализация sum имеет интересную особенность: мы решили сделать так, что при вызове без аргументов возвращается значение 0 ("Ничего"), при вызове с одним числом возвращается это же число. Хотя можно было обработать эти ситуации как-то по-другому (например, вернуть специальное значение null), это вопрос удобного выбора, подходящего для решения конкретных практических задач.

В таком контексте rest-массив можно считать, как "необязательные аргументы", которые можно либо вовсе не передавать, либо передавать столько, сколько хочешь. А что, если мы захотим, чтобы функция имела два обыкновенных ("обязательных") именованных параметра, а остальные были необязательными и сохранялись в 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]

https://repl.it/@hexlet/js-functions-rest-operator-many-more-args

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

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

и для трёх:

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

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

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

И такой тоже:

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

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


<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

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

Для полного доступа к курсу, нужна профессиональная подписка

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

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

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

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг».

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

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг».