Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Выполнение функций JS: Функциональное программирование

Вспомним несколько важных понятий из прошлого курса:

Выражение — код, выполнение которого возвращает значение. Инструкция — код, представляющий собой команду.

  • Выражения — вычисляются.
  • Инструкции — исполняются.

К выражениям относятся:

  • Вызов функции
  • Арифметические и логические операции
  • Тернарный оператор
  • и другие

К инструкциям относятся:

  • for
  • while
  • break
  • return
  • if
  • и другие

Список выражений и инструкций на этом не заканчивается, с полным перечнем можно ознакомиться в документации (см. дополнительные материалы к уроку).

Для лучшего понимания, как могут взаимодействовать друг с другом различные конструкции языка, необходимо знать разницу между ними.

Возьмем условный оператор if. В некоторых языках он представлен инструкцией, в некоторых — выражением. Посмотрите на код ниже и подумайте, возможно ли такое в JavaScript и почему:

const value = if (something) {
    one
  } else {
    two
  };

Такой код в JavaScript невозможен только по одной простой причине: ifэто инструкция, а не выражение.

Но такой код возможен и часто используется в языках вроде Ruby или Python. Зачем же нужен if, если есть тернарный оператор, который как раз и является выражением?

Дело вот в чем. Если бы if был выражением, то тернарный оператор стал бы попросту не нужен, несмотря на то, что он является более лаконичной заменой if. Но и тернарный оператор бывает неудобен в тех ситуациях, когда вычисление слишком большое и не помещается в одну строку, а его результат должен быть записан в одну и ту же константу или переменную в каждой из веток. В такой ситуации как раз пригодился бы if как выражение.

Этот пример ярко иллюстрирует тот факт, что конструкции языка, представленные выражением, делают язык гибче, а решения с их использованием делают код лаконичнее. Другими словами, язык становится выразительнее. Кроме описанного выше, у выражений есть еще одно огромное преимущество: они могут комбинироваться друг с другом и вкладываться друг в друга.

Ниже я продемонстрирую варианты комбинирования выражений, но с акцентом на функции:

Арифметические операции

const r1 = 5;
const r2 = 5 + 8;
const r3 = 5 + 8 - Math.PI;
const r4 = 5 + 8 - Math.PI * Math.sqrt(16);
const r5 = f1() + f2() * f3();

Вопросы могут возникнуть только в последней строчке. Насколько это допустимо? Вызов функции — это выражение, возвращающее результат, так что этот код допустим на 100%. Если функция вернет значение, неподходящее для сложения, то может возникнуть ошибка, но это будет логическая ошибка, а не синтаксическая.

Примечание: предполагается, что используемые в примерах этого урока функции и константы (например, f2 или isEditing) ранее где-то были определены. Их определение мы убрали из примеров, чтобы не отвлекать от главного.

Логические операции

Все то же самое можно делать и с логическими выражениями:

const r1 = true;
const r2 = true || false;
const r3 = true || false && isEditing;
const r4 = true || false && isEditing || isEmpty(data);
const r5 = f1() || f2() && f3();

Из-за слабой типизации подобный код работать будет вообще всегда, даже если функции возвращают не true или false, но пользоваться этим не стоит. (Типизация рассматривалась в уроке предыдущего курса).

Аргументы

А теперь чуть более сложный пример. Когда мы вызываем функцию, то в аргументах ожидается выражение: func(<expression>, <expression>, ...). А из этого следует, что мы можем сделать так:

const r1 = f();
const r2 = f(5);
const r3 = f(5 + Math.PI);
const r4 = f(5 + Math.PI - cube(number));
const r5 = f(f1(f2(n3, f3(n1, n2))), f4());

Если последний вызов вам кажется сложным, то вспомните вот что. Когда мы изучали русский язык в школе, то постоянно делали грамматический разбор предложения, в котором выделяли существительное, подлежащее, сказуемое, различные обороты и многое другое. Здесь нужно сделать то же самое. Очень важно уметь разбивать в уме сложное выражение на составные.

Пройдемся по примеру выше. f(f1(f2(n3, f3(n1, n2))), f4()) содержит в аргументах два выражения:

  1. f4()
  2. f1(f2(n3, f3(n1, n2)))
    • этот вызов содержит один аргумент — вызов функции: f2(n3, f3(n1, n2)),
      • который в свою очередь содержит два аргумента: n3 и вызов функции f3(n1, n2).

Порядок выполнения

Осталось понять, в какой последовательности происходят эти вызовы. JavaScript считается энергичным языком, то есть языком с аппликативным порядком вычисления, а это значит, что аргументы вычисляются до того, как попадают внутрь функций.

const f1 = () => console.log('called f1');
const f2 = () => console.log('called f2');
const f3 = () => console.log('called f3');
const f4 = () => console.log('called f4');
const f5 = () => console.log('called f5');
const f6 = () => console.log('called f6');

f1(f2(f4(), f5()), f3(f6()));

// => called f4
// => called f5
// => called f2
// => called f6
// => called f3
// => called f1

https://repl.it/@hexlet/js-functions-execution-nested-calls

Как видно, вычисление идет с самого глубокого уровня слева направо. Каждая функция попадает в стек вызовов, где работает принцип "первым вошёл - последним вышел". Поскольку каждый аргумент - тоже функция, то они также добавляются за ней следом. Те функции, которые возвращают результат, сразу вычисляются. Так получается, что стек наполняется и очищается до тех пор, пока не выполнится f1.


Дополнительные материалы

  1. Выражения и операторы
  2. Инструкции и объявления

Аватары экспертов Хекслета

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

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

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

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

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

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

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

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

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

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

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

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

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и соглашаетесь с «Условиями использования»