JavaScript | Функция и несколько операторов return

JavaScript | Функция и несколько операторов return

Прежде чем погружаться в тему публикации, давайте проясним два момента:

  1. Функция может не иметь оператор return в своём теле
  2. Функция может иметь хотя бы один оператор return в своём теле

То есть, существует всего два варианта. Функция что-то возвращает в определённый момент своей работы или не возвращает.

 

Видео про несколько операторов return в теле функции на JavaScript

 

Ниже две функции. У f1() в теле ничего не происходит и такая функция даёт «undefined». У f2() а теле только оператор return и мы также получаем «undefined».

function f1(){}

function f2(){return}

f1()
undefined

f2()
undefined
Пустая функция с оператором return и без него - JavaScript
Пустая функция с оператором return и без него — JavaScript

Мы будем рассматривать тот случай, когда функция содержит в себе несколько операторов return.

Новичку, изучающему JavaScript, бывает трудно понять, как в функции может быть несколько операторов return. На этом важно сделать акцент.

 

Самый простой пример функции на JavaScript, у которой в теле друг за другом прописаны 5 операторов return

Я создам функцию, тело которой будет иметь символы переноса строки. Каждый оператор return будет записан новую строку.

function f3(){
   return
   return
   return
   return
   return
};

Обратите внимание!!! Мы не можем после оператора return написать ещё один оператор return, если оба этих оператора находятся на одной линии (если между ними нет символа переноса строки или точки с запятой). Данная запись будет являться синтаксической ошибкой. Когда среда выполнения кода дойдёт до анализа и регистрации этой функции — программа сломается.

function f4(){return return return return return}

Такая запись объявления функции в одну строчку с последовательными операторами return невозможна по правилам самого языка.

Символы переноса строки между операторами return в JavaScript
Символы переноса строки между операторами return в JavaScript

Чтобы подобной ошибки не возникало, нужно обязательно ставить символ «точка с запятой» — «;«.

 

Вызываем функцию f3()

Давайте сейчас сделаем вызов нашей функции f3(), где стояли символы переноса строк.

function f3(){
   return
   return
   return
   return
   return
};

f3()
undefined

Функция отрабатывает корректно и в ответ мы получаем «ничего» — undefined. Давайте разберёмся, что тут произошло.

Вызвали функцию с 5 return и символами переноса строки в JavaScript
Вызвали функцию с 5 return и символами переноса строки в JavaScript

Прежде всего нужно смотреть на синтаксис работы оператора returnJavaScript полностью опирается на стандарт ECMAScript в своей работе.

Обратите внимание, что оператор return должен всегда сопровождаться «точкой с запятой» справа от себя. Но наша функция f3() не содержала ни одной «точки с запятой» в теле. Почему же тогда наша функция вообще отработала корректно и не выдала ни одной ошибки?

Синтаксис оператора return в JavaScript по стандарту ECMAScript 2023
Синтаксис оператора return в JavaScript по стандарту ECMAScript 2023

 

Есть нюанс, о котором нужно знать.

Когда среда выполнения кода получила нашу функцию с символами переноса строк между операторами return, то в этот момент она (среда) попыталась произвести автоматическую подстановку с заменой на «точку с запятой» — «;«. Мы как разработчики этого не видим, но это происходит «под капотом».

Ниже информация из документации стандарта ECMAScript:

Большинство операторов и объявлений ECMAScript должны заканчиваться «точкой с запятой». Такие «точки с запятой» всегда могут явно появляться в исходном тексте. Однако для удобства такие «точки с запятой» могут быть опущены в исходном тексте в определенных ситуациях. Эти ситуации описываются тем, что в таких ситуациях точка с запятой автоматически вставляется в поток «токенов» исходного кода.

В следующих правилах «токен« означает фактическую распознанную лексическую лексему, определённую с использованием текущего символа лексической цели, как описано в пункте 12 стандарта ECMAScript.

Есть три основных правила вставки точки с запятой:

1 — Когда при анализе исходного текста слева направо обнаруживается токен (называемый оскорбительным токеном), который не разрешён ни одним производством из  грамматики, то перед оскорбительным токеном автоматически вставляется точка с запятой, если выполняется одно или несколько следующих условий:

Оскорбительный токен отделён от предыдущего токена по крайней мере одним LineTerminator.

Оскорбительный токен является правой фигурной скобкой — «}«.

Предыдущий токен является правой круглой скобкой — «)«, и вставленная точка с запятой будет затем проанализирована как завершающая точка с запятой оператора do-while.

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

3 — Когда при синтаксическом анализе исходного текста слева направо встречается токен, который разрешён некоторым производством грамматики, но производство является ограниченным производством (restricted production), и токен будет первым токеном для терминала или нетерминала сразу после аннотацию «[здесь нет LineTerminator]» внутри ограниченного производства (поэтому такой токен называется ограниченным токеном), и ограниченный токен отделяется от предыдущего токена хотя бы одним LineTerminator, то точка с запятой автоматически вставляется перед ограниченным токеном.

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

Оператор return относится к ограниченному производству в грамматике, поэтому к нему применяется третье правило.

 

Почему отработало без «точек с запятыми» мы поняли. Но что случилось дальше?

Оператор return относится к операторам «внезапного завершения» функции — (abrupt completion). Это значит, что после выполнения внезапного завершения на первом операторе return, все остальные операторы не выполнились. После первого return функция уже не работала. До второго return мы просто не дошли. Можно сказать, что он был проигнорирован.

Эта ситуация наглядно показывает нам одну важную особенность в написании кода. В стандарте ECMAScript существует много разных понятий. Одно из них называется «Block«.

Логическое разделение функции на блоки создаёт возможность использовать несколько независимых операторов return, которые никак не конфликтуют друг с другом и не перекрывают логику работы всей функции.

 

Понятие Block в написании кода на JavaScript

Понятие Блок (Block) для JavaScript по стандарту ECMAScript
Понятие Блок (Block) для JavaScript по стандарту ECMAScript

 

Идея блоков не нова. Они присутствуют во многих языках программирования.

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

Важно тут понять последние два элемента:

  1. Statement (Оператор)
  2. Declaration (Объявление)

 

Ещё две функции для примера:

function f5(){
   if(true){return 1}
   else{return 2}
}

function f6(){
   if(false){return 1}
   else{return 2}
}

Вызовы данных функций реагируют на разные операторы return.

В случае с функцией f5() мы получаем 1, а с f6() получаем 2.

Получается, что внутри тела функции операторы return не знают о существовании друг друга. Они не идут последовательно друг за другом. Конструкция с ветвлением переводит выполнение алгоритма в зависимости от условия.

Два оператора return при производстве IfStatement - JavaScript
Два оператора return при производстве IfStatement — JavaScript

Получается, что любая функция может иметь в своём теле большое количество операторов return.

Тут главное самому разработчику не запутаться и не потерять скобки.

 

Другой пример, который также имитирует ветвление — конструкция перехвата ошибки «try catch«:

function f7(a){
  try {
    return a.w
  } catch (error) {
    return 'Ошибка. Нет А'
  }
}

Делаем вызовы:

f7()
f7({})
f7({w:1})

Скриншоты:

Два оператора return при производстве TryStatement - JavaScript
Два оператора return при производстве TryStatement — JavaScript

Функция f7() содержит всего два оператора return, но фактические значения возвращаемые функцией могут быть разнообразными.

 

И финальный вариант, в котором используются два оператора: IF и TRY.

function f8(a){
  try {
    if(a.w==1){return a.w}
    return 888
  } catch (error) {
    return 'Ошибка. Нет А'
  }
}

Результаты вызовов:

Три оператора return и два производства - TryStatement и IfStatement - JavaScript
Три оператора return и два производства — TryStatement и IfStatement — JavaScript