return: результат функции
До сих пор наши функции в основном печатали результат через console.log. Но иногда результат нужен не человеку, а самой программе – например, чтобы сохранить его в переменную, использовать в другом вычислении или проверить в условии.
Для этого функция должна вернуть значение. В JavaScript это делает return:
function orderTotal(price, count) { return price * count;}const total = orderTotal(90, 3);console.log(total);orderTotal(90, 3) возвращает 270, поэтому после выполнения весь вызов можно воспринимать как обычное значение:
const total = orderTotal(90, 3);const total = 270;После return можно указать готовое значение, переменную или выражение:
return 300;return price;return price * count;В последнем случае сначала вычислится price * count, а функция вернёт получившийся результат. За один вызов функция возвращает одно значение. Посмотрим весь путь значения по шагам:
90 и 3 легли в параметры.console.log показывает, return возвращает
Здесь легко запутаться. Если функция что-то напечатала в консоли, может показаться, что это и есть её результат. Но вывод в консоль и результат функции – разные вещи.
Посмотрим на обычную функцию суммы:
function sum(a, b) { console.log(a + b);}const result = sum(2, 3);console.log(result);Первую 5 напечатала сама функция – сработал console.log(a + b). Но затем мы отдельно попросили вывести result и получили undefined. Значит, функция показала сумму в консоли, но наружу её не передала.
Чтобы результат вызова можно было сохранить и использовать дальше, его нужно вернуть:
function sum(a, b) { return a + b;}const result = sum(2, 3);console.log(result);Теперь всё иначе: sum(2, 3) возвращает 5, и это значение попадает в result. Поэтому полезно разделять две вещи: console.log(...) показывает значение человеку, return отдаёт значение программе.
С результатом вызова можно работать
После return вызов функции ведёт себя как обычное значение. Его необязательно сначала сохранять в переменную – можно использовать там же, где вы использовали бы число, строку или другое значение.
Сохранить:
const price = double(50);Использовать в выражении или условии:
const finalPrice = double(50) + 20;if (double(60) > 100) { console.log('Дорого');}Передать другой функции:
addTen(double(5));Сначала double(5) возвращает 10, затем получается addTen(10), который возвращает 20. Так функции соединяются друг с другом: одна возвращает результат, а другая использует его дальше.
Но у вложенности есть цена – читаемость: чем больше вызовов внутри вызовов, тем труднее следить за порядком вычислений. Если цепочка разрастается, промежуточный результат лучше положить в переменную с понятным именем:
const step1 = addTen(double(5)); // 20// 50const step2 = addTen(double(step1));console.log(step2);console.log(addTen(double(addTen(double(5)))));Возвращать можно разные значения
function isAdult(age) { return age >= 18;}console.log(isAdult(20));console.log(isAdult(14));Здесь выражение age >= 18 даёт true или false, поэтому одно из этих значений и становится результатом функции. Позже функции будут возвращать также строки, массивы, объекты и другие значения.
До сих пор мы смотрели на return как на способ получить результат из функции. Но у него есть ещё одно важное свойство: как только JavaScript встречает return, текущий вызов заканчивается. Всё, что находится ниже, в этом вызове уже не выполняется:
function test() { console.log('до'); return 5; console.log('после');}console.log(test());Функция успела напечатать до, затем дошла до return 5 и сразу закончила работу. Поэтому строка console.log('после') недостижима.
При этом следующий вызов test() снова начнёт выполнение функции с первой строки: завершается вызов, а не сама функция.
Иногда нужно просто выйти
Если главная задача return в конкретном месте – остановить функцию, возвращать значение необязательно. Можно написать просто return; – вызов закончится точно так же, а его результатом станет undefined:
function submitOrder(total) { if (total <= 0) { console.log('Корзина пуста'); return; } console.log('Заказ отправлен');}submitOrder(1200);submitOrder(0);Если сумма заказа равна нулю, отправлять заказ дальше бессмысленно. Функция сообщает о проблеме и заканчивает текущий вызов через return. Поэтому до строки console.log('Заказ отправлен') пустая корзина вообще не доберётся.
Ту же проверку можно записать и привычным if..else – обе версии работают одинаково:
if (total <= 0) { console.log('Корзина пуста'); return;}console.log('Заказ отправлен');if (total <= 0) { console.log('Корзина пуста');} else { console.log('Заказ отправлен');}Пока проверка одна, оба варианта читаются одинаково хорошо – выбирайте любой. Разница появляется, когда проверок несколько: каждая новая ветка else вкладывает код на уровень глубже, а return держит все проверки на одном уровне. Это и показывает следующая секция.
Допустим, заказ можно оформить, только если он оплачен и указан адрес. Без раннего выхода можно написать так:
function checkout(isPaid, hasAddress) { if (isPaid) { if (hasAddress) { return 'Заказ оформлен'; } else { return 'Укажите адрес'; } } else { return 'Сначала оплатите заказ'; }}Это работает. Но читать такую функцию неудобно: основная логика всё глубже уезжает вправо. Проще сначала проверить причины, по которым продолжать нельзя. Условия при этом переворачиваются: в начало функции поднимается то, что раньше лежало в ветках else, – и каждый такой случай сразу завершает вызов через return:
function checkout(isPaid, hasAddress) { if (!isPaid) { return 'Сначала оплатите заказ'; } if (!hasAddress) { return 'Укажите адрес'; } return 'Заказ оформлен';}console.log(checkout(true, false));console.log(checkout(true, true));Функция идёт сверху вниз. Если заказ не оплачен – ответ уже известен, поэтому продолжать незачем: return сразу завершает вызов. Если с оплатой всё хорошо, проверяем адрес. Нет адреса – снова сразу выходим. До последней строки доходят только заказы, которые прошли обе проверки.
Плохой случай? Разобрались с ним и вышли. Хороший случай идёт дальше.
// теперь вы знаете
- return вычисляет выражение справа и возвращает получившееся значение – одно за вызов
- Вызов функции становится значением: его можно сохранить, сравнить, передать дальше
- console.log показывает значение человеку, return отдаёт его программе
- return завершает текущий вызов: код ниже не выполняется, а return; возвращает undefined
- Ранний выход: плохой случай – разобрались и вышли, хороший идёт дальше
Что появится в консоли?
function double(x) {return x * 2;}console.log(double(4));
Что появится в консоли?
function hello() {console.log('привет');}console.log(hello());
Что НЕ появится в консоли?
function test() {console.log('до');return 5;console.log('после');}console.log(test());
Появится ли в консоли «Заказ отправлен»?
function submitOrder(total) {if (total <= 0) {return;}console.log('Заказ отправлен');}submitOrder(0);