Параметры, аргументы и значения по умолчанию
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Параметры — это ярлыки на пустых ящиках, которые функция расставляет заранее; аргументы — то, что вы кладёте в эти ящики при вызове. Разберёмся, что произойдёт, если ящиков больше, чем вещей, и почему переданный внутрь объект можно случайно испортить.
Параметры — имена в скобках при объявлении функции. Это обычные локальные переменные, которые появляются в момент вызова. Аргументы — конкретные значения, которые вы подставляете, когда вызываете функцию.
Связь между ними — позиционная. Первый аргумент попадает в первый параметр, второй — во второй, и никакие имена переменных снаружи на это не влияют. Поэтому перепутанный порядок — не ошибка синтаксиса, а тихо неверный ответ.
Параметр — имя в объявлении, аргумент — то, что реально приехало при вызове. В разговоре их путают все, но на собеседовании спрашивают.
Если аргумент могут не передать, задайте параметру запасное значение прямо в объявлении: параметр = значение. Оно подставится только тогда, когда аргумента нет.
Умолчание — это выражение, которое вычисляется при каждом вызове. Значит, в нём можно ссылаться на параметры, объявленные левее, и даже вызывать функции. Справа налево не работает: параметр, до которого очередь ещё не дошла, недоступен.
Движок сравнивает аргумент именно с undefined. Пустой вызов и явный undefined для него одно и то же, а вот null — полноценное значение, и функция примет его как есть.
JavaScript не сверяет количество аргументов с количеством параметров. Передали больше — лишнее просто некуда положить, и оно отбрасывается. Передали меньше — оставшиеся параметры получат undefined. Ошибки не будет ни в том, ни в другом случае, будет странный результат. А сколько параметров объявлено, знает сама функция: sum.length.
| Вызов | a | b | Результат |
|---|---|---|---|
| sum(1, 2) | 1 | 2 | 3 |
| sum(1, 2, 3) | 1 | 2 | 3 |
| sum(1) | 1 | undefined | NaN |
| sum() | undefined | undefined | NaN |
Тогда соберите их в массив остаточным параметром ...имя — он всегда последний в списке. Старый способ, псевдомассив arguments, ещё встречается в чужом коде, но в новом его не пишут: это не настоящий массив, а у стрелочной функции своего arguments нет вовсе. Обе роли трёх точек разбирает статья «Spread и rest: три точки в двух ролях».
Число, строка, булево значение попадают в параметр копией. Внутри функции вы работаете со своей коробкой: можно присваивать в неё что угодно, наружная переменная об этом не узнает.
Это удобная гарантия: функция, принимающая примитивы, физически не может испортить данные вызывающего кода. Всё, что она хочет сообщить, она возвращает через return.
С объектами и массивами копируется не содержимое, а ссылка — адрес одного и того же объекта в памяти. Параметр и внешняя переменная указывают в одну точку, поэтому изменение поля внутри функции видно снаружи.
А вот переприсвоить параметр целиком бесполезно: вы перенаправите только свою копию ссылки, внешняя переменная останется смотреть на старый объект. Отсюда простое правило: менять поля чужого объекта — можно, но лучше явно возвращать новый.
Когда параметров становится больше трёх, вызов вида createUser("Аня", true, false, 5) невозможно прочитать. Приём: принимать один объект и сразу разбирать его в скобках функции — умолчания при этом работают как обычно, а порядок перестаёт иметь значение.
Одна тонкость: вызов createUser() вообще без аргумента упадёт — разбирать нечего. Лечится умолчанием для всего объекта: function createUser({ name, role = "user" } = {}). Разбор самого узора — в статье «Деструктуризация»: там же и про вложенные структуры, и про новое имя переменной.
- Параметры объявляются в скобках функции, аргументы передаются при вызове; связываются они по позиции, а не по именам переменных снаружи.
- Запись «параметр = значение» подставляет умолчание только вместо undefined; null умолчание не включает, а само выражение вычисляется при каждом вызове.
- Лишние аргументы молча отбрасываются, недостающие параметры становятся undefined — ошибки не будет, будет неверный результат.
- Примитив попадает в параметр копией: что бы функция с ним ни делала, внешняя переменная не изменится.
- Объект передаётся копией ссылки: менять его поля внутри — значит менять оригинал, а переприсваивать параметр целиком — бессмысленно.
1/3Что выведет console.log(f(3)) для function f(a, b = a * 2) { return a + b; }?
2/3function hi(name = "гость") { return name; } — что вернёт hi(null)?
3/3function f(obj) { obj = { n: 2 }; } — const o = { n: 1 }; f(o); чему равно o.n?
Цена со скидкой
Допишите функцию price(sum, discount): она возвращает сумму, уменьшенную на discount процентов и округлённую до целого. Если скидку не передали, цена остаётся прежней.
Умолчание 0 делает вызов без второго аргумента корректным: вычитаем ноль процентов и получаем исходную сумму, никаких if не нужно.
Не испортить чужой объект
Функция withBonus должна вернуть новый объект с полем score, увеличенным на 10, и при этом не тронуть исходный. Сейчас она возвращает тот же самый объект.
Спред-синтаксис копирует поля в новый объект, а score перезаписывается уже в копии. Исходный user остаётся нетронутым — именно поэтому такой стиль безопаснее, чем user.score += 10.