Функциональные выражения
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Функцию в JavaScript можно не только объявить, но и присвоить переменной — ровно так же, как число или строку. Из этой маленькой разницы вырастают колбэки, обработчики событий и половина приёмов, которые вы встретите в чужом коде.
До сих пор мы писали функции так: ключевое слово function, имя, скобки, тело. Это Function Declaration — объявление функции. Но есть и второй способ: написать функцию справа от знака = и положить её в переменную. Это Function Expression — функциональное выражение.
Обе функции ведут себя одинаково: вызываются круглыми скобками, принимают аргументы, возвращают результат. Разница не в том, что они умеют, а в том, чем является строка кода. Объявление — самостоятельная конструкция языка. Выражение — часть инструкции присваивания, поэтому в конце стоит точка с запятой: она закрывает не функцию, а const mul = ....
Функция в JavaScript — обычное значение, такое же как число 5 или строка. Её можно положить в переменную, передать в другую функцию, вернуть из функции, сложить в массив. Отличие от числа ровно одно: значение-функцию можно вызвать, добавив скобки.
Скобки — это вызов. greet — сама функция, greet() — её результат. Перепутать легко: const hello = greet(); положит в переменную не функцию, а то, что она вернула.
Поэтому строка const hello = greet; и работает: справа стоит готовое значение, оно копируется в новую переменную. Теперь на одну и ту же функцию смотрят два имени — как две этикетки на одной коробке.
Прежде чем выполнять код, движок просматривает его целиком и заранее создаёт все функции, объявленные через Function Declaration. Их словно поднимают в начало области видимости — это называют всплытием (hoisting). Поэтому объявленную функцию можно вызвать выше, чем она написана.
С функциональным выражением так не выйдет. Пока выполнение не дошло до строки с присваиванием, переменная объявлена, но значения в ней нет — обращение к ней даёт ReferenceError. Функция появляется ровно в тот момент, когда движок доходит до =. Подробный разбор обоих механизмов — в статье «Всплытие и временная мёртвая зона»; здесь достаточно правила: объявление вызвать выше можно, выражение — нет.
Ошибка будет другой и оттого коварнее: var всплывает со значением undefined, поэтому вызов до присваивания упадёт с TypeError — «cube is not a function». Переменная как бы есть, а функции в ней нет. const и let в этом смысле честнее: они сразу сообщают, что значение ещё не готово.
В примере const mul = function (a, b) {...} между function и скобками нет имени. Такая функция называется анонимной. Имя ей и не требуется: обращаться к ней мы будем через переменную mul.
Чаще всего анонимные функции пишут прямо в месте использования — когда функцию нужно один раз передать другой функции. Такой аргумент-функцию называют колбэком. Придумывать ему имя было бы лишней работой: он живёт ровно одну строку. Приёму целиком посвящена статья «Колбэки: функция как аргумент».
Функциональному выражению разрешено дать имя — это Named Function Expression. Имя видно только внутри самой функции: снаружи такой переменной не существует. Удобно, когда функция вызывает саму себя и не должна зависеть от внешнего имени.
Практическое правило простое. Если функция — самостоятельный кусок логики, объявляйте её через function имя() {}: имя видно сразу, а порядок функций в файле можно выбирать по смыслу, а не по зависимостям. Функциональное выражение берите тогда, когда функция должна быть значением: колбэк, обработчик, элемент массива, функция, которую выбирают условием.
| Признак | Function Declaration | Function Expression |
|---|---|---|
| Как выглядит | function sum(a, b) { ... } | const sum = function (a, b) { ... }; |
| Доступна выше своей строки | да, всплывает | нет, будет ошибка |
| Точка с запятой в конце | не нужна | нужна |
| Имя | обязательно | необязательно, обычно анонимная |
Ни один из способов не «правильнее» другого. Это два инструмента: первый даёт имя и свободу порядка, второй — гибкость значения, которое можно передать куда угодно.
- Function Declaration — самостоятельное объявление function имя() {}; Function Expression — функция справа от знака = , часть инструкции присваивания (и потому с точкой с запятой в конце).
- Объявления всплывают: их можно вызвать выше по коду. Функциональные выражения — нет: до строки с присваиванием обращение даёт ReferenceError.
- Функция — обычное значение: её можно скопировать в другую переменную, передать аргументом, вернуть из функции.
- Имя без скобок — сама функция, имя со скобками — её вызов и результат.
- Функция без имени называется анонимной; чаще всего такие пишут прямо на месте, передавая колбэком в другую функцию.
1/3Что произойдёт при запуске: say(); const say = function () { console.log(1); };
2/3Чем const f = greet; отличается от const f = greet();
3/3Почему после функционального выражения ставят точку с запятой, а после объявления функции — нет?
Переписать объявление в выражение
Перепишите функцию area как функциональное выражение в const так, чтобы код по-прежнему выводил 12. Подсказка: одной заменой синтаксиса дело не обойдётся.
Функциональное выражение не всплывает, поэтому вызов нужно перенести ниже присваивания:
Если оставить console.log сверху, получится ReferenceError: до строки с = значения в area ещё нет. И не забудьте точку с запятой после закрывающей фигурной скобки — она завершает инструкцию присваивания.
Функция как аргумент
Напишите функцию applyTwice(fn, x), которая применяет переданную функцию к значению дважды: сначала к x, потом к полученному результату.
Параметр fn — обычная переменная, в которой лежит функция-значение. Чтобы её выполнить, добавляем скобки: fn(x). Внутренний вызов даёт 10, внешний превращает его в 20.
Такую же анонимную функцию можно сначала положить в переменную и передать по имени — результат не изменится.