Главная страница » Что такое функции в javascript

Что такое функции в javascript

  • автор:

Функции — часть первая

Функция – это блок программного кода на языке JavaScript, который определяется один раз и может выполняться, или вызываться, многократно.

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

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

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

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

example img

Люди считают, что компьютерные науки – это искусство для гениев. В реальности всё наоборот – просто множество людей делают вещи, которые стоят друг на друге, будто составляя стену из маленьких камушков.

2. Объявление функций

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

— Идентификатор, определяющий имя функции. Имя является обязательной частью инструкции объявления функции;
— Пара круглых скобок вокруг списка из нуля или более идентификаторов, разделенных запятыми. Эти идентификаторы будут определять имена параметров функции и в теле функции могут использоваться как локальные переменные.;
— Пара фигурных скобок с нулем или более инструкций JavaScript внутри. Эти инструкции составляют тело функции: они выполняются при каждом вызове функции.

Обратите внимание на то, что объявление функции не заканчивается точкой с запятой (;).

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

3. Вызов функций

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

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

При вызове функции вычисляются все выражения-аргументы (указанные между скобками), и полученные значения используются в качестве аргументов функции. Эти значения присваиваются параметрам, имена которых перечислены в определении функции. В теле функции выражения обращений к параметрам возвращают значения соответствующих аргументов.

4. Аргументы и параметры функций

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

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

Функцию можно вызвать с любым количеством аргументов. Если параметр не передан при вызове – он считается равным undefined.

4.1 Аргументы по умолчанию

Для указания значения «по умолчанию», то есть, такого, которое используется, если аргумент не указан можно использовать такой приём: проверяем равен ли аргумент undefined, и если получаем true – записываем в него значение по умолчанию.

5. Значение, возвращаемое функцией

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

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

В коде выше, если сработал if, то строка (*) и весь код под ней никогда не выполнится, так как return завершает выполнение функции.

Если функция возвращает значение (а функция в JavaScript всегда возвращает значение), то вызов функции рассматривается как выражение, значение которого равно величине, возвращаемой функцией. Мы можем использовать это выражение аналогично любым другим выражениям; например, мы можем присвоить его значение переменной:

6. Локальные переменные

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

Неважно, где именно в функции объявляется переменная. Любое объявление срабатывает один раз и распространяется на всю функцию. Такое поведение var внутри функций называется всплытие, эго мы рассмотрим позже.

7. Внешние переменные

Функция может обратиться ко внешней переменной, например:

Доступ возможен не только на чтение, но и на запись. При этом, так как переменная внешняя, то изменения будут видны и снаружи функции:

Если бы внутри функции, в строке (1), была бы объявлена своя локальная переменная var userName, то все обращения использовали бы её, и внешняя переменная осталась бы неизменной.

Более детально механизм взаимодействия c внешними переменными мы рассмотрим позже.

8. Всплытие внутри функций

Так сложилось, что одна из самых хитрых частей программирования на JavaScript — это локация и способ объявления переменных. В большинстве С-подобных языков переменные (или их связывания) создаются там, где их объявляют. Однако в JavaScript это не совсем так. Используя var объявления, переменная перемещается на начало функции (или в начало глобальной области видимости, если объявление состоялось вне функции) независимо от того, где оно произошло на самом деле — это называется всплытие. Чтобы продемонстрировать это поведение, рассмотрим следующую функцию:

Если вы не знакомы с JavaScript, скорее всего вы ожидаете, что переменная value будет создана только тогда, когда condition будет равняться true. На самом деле value будет создана независимо от этого. Под капотом движок JavaScript изменяет функцию getValue таким образом, что она выглядит так:

Переменная value всплывает вверху блока и в том же месте происходит инициализация. Это означает, что переменная value все еще доступна в блоке else. Однако, если обратиться к ней, получим undefined, потому что она еще не проинициализирована.

Часто новичкам в JavaScript-разработке трудно привыкнуть к тому, как работает объявления переменных, и непонимание этой неочевидной поведения может привести к ошибкам.

9. Именование функций

В качестве имени функции может использоваться любой допустимый идентификатор. Старайтесь выбирать функциям достаточно описательные, но не длинные имена. Искусство сохранения баланса между краткостью и информативностью приходит с опытом. Правильно подобранные имена функций могут существенно повысить удобочитаемость (а значит, и простоту сопровождения) ваших программ.

Чаще всего в качестве имен функций выбираются глаголы или фразы, начинающиеся с глаголов. По общепринятому соглашению имена функций начинаются со строчной буквы. Если имя состоит из нескольких слов, в соответствии с одним из соглашений они отделяются друг от друга символом подчеркивания, примерно так: like_this(), по другому соглашению все слова, кроме первого, начинаются с прописной буквы, примерно так: likeThis(). Имена функций, которые, как предполагается, реализуют внутреннюю, скрытую от посторонних глаз функциональность, иногда начинаются с символа подчеркивания.

— Имя функции должно понятно и чётко отражать, что она делает. Увидев её вызов в коде, вы должны тут же понимать, что она делает.
— Функция – это действие, поэтому для имён функций, как правило, используются глаголы.

10. Резюме

Функция – это блок программного кода на языке JavaScript, который определяется один раз и может выполняться, или вызываться, многократно.

Функции позволяют обеспечить внутреннюю организованность программы и сократить размер ее кода, присваивая повторяющимся фрагментам программы имя и заменяя этим именем все повторения, встречающиеся в программе. Объявление функции задает общий вид функции(сигнатуру) и совокупность действий(тело), выполняемых функцией. Вызов функции передает управление в функцию.

Когда выполнение функции завершается, она может возвратить значение программе, которая ее вызвала. Функция может возвращать только одно значение.

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

Функции являются основными строительными блоками скриптов. Мы будем неоднократно возвращаться к ним и изучать все более и более глубоко.

11. Упражнения

1. Минимум

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

2. Even number

Создайте функцию isEven, которая возвращает значение true или false в зависимости от того является ли аргумент x четным.

3. dayOfWeek

Создайте функцию, которая принимает в себя число x э [1; 7] и возвращает название дня недели. Используйте switch-case.

4. calcBox

Создайте функцию, которая принимает в себя 3 аргумента и рассчитывает объем коробки.

5. hmsToSecs

Напишите функцию hmsToSecs, имеющую три аргумента h, m, s. Функция должна возвращать эквивалент переданного ей временного значения в секундах. Создайте программу, которая будет циклически запрашивать у пользователя ввод значения часов, минут и секунд и выводить результат работы функции на экран.

6. callCounter

Напишите функцию, которая при каждом вызове будет выводить на экран количество раз, которое она вызывалась ранее. Напишите программу, которая будет вызывать данную функцию не менее 10 раз.
(Задача на использование глобальных переменных)

7. primeNumber

Напишите функцию, которая которая принимает в себя натуральное число n и возвращает самое большое простое число в диапазоне (2; n).

Функции

Зачастую нам надо повторять одно и то же действие во многих частях программы.

Например, необходимо красиво вывести сообщение при приветствии посетителя, при выходе посетителя с сайта, ещё где-нибудь.

Чтобы не повторять один и тот же код во многих местах, придуманы функции. Функции являются основными «строительными блоками» программы.

Примеры встроенных функций вы уже видели – это alert(message) , prompt(message, default) и confirm(question) . Но можно создавать и свои.

Объявление функции

Для создания функций мы можем использовать объявление функции.

Пример объявления функции:

Вначале идёт ключевое слово function , после него имя функции, затем список параметров в круглых скобках через запятую (в вышеприведённом примере он пустой) и, наконец, код функции, также называемый «телом функции», внутри фигурных скобок.

Наша новая функция может быть вызвана по своему имени: showMessage() .

Вызов showMessage() выполняет код функции. Здесь мы увидим сообщение дважды.

Этот пример явно демонстрирует одно из главных предназначений функций: избавление от дублирования кода.

Если понадобится поменять сообщение или способ его вывода – достаточно изменить его в одном месте: в функции, которая его выводит.

Локальные переменные

Переменные, объявленные внутри функции, видны только внутри этой функции.

Внешние переменные

У функции есть доступ к внешним переменным, например:

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

Внешняя переменная используется, только если внутри функции нет такой локальной.

Если одноимённая переменная объявляется внутри функции, тогда она перекрывает внешнюю. Например, в коде ниже функция использует локальную переменную userName . Внешняя будет проигнорирована:

Переменные, объявленные снаружи всех функций, такие как внешняя переменная userName в вышеприведённом коде – называются глобальными.

Глобальные переменные видимы для любой функции (если только их не перекрывают одноимённые локальные переменные).

Желательно сводить использование глобальных переменных к минимуму. В современном коде обычно мало или совсем нет глобальных переменных. Хотя они иногда полезны для хранения важнейших «общепроектовых» данных.

Параметры

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

В нижеприведённом примере функции передаются два параметра: from и text .

Когда функция вызывается в строках (*) и (**) , переданные значения копируются в локальные переменные from и text . Затем они используются в теле функции.

Вот ещё один пример: у нас есть переменная from , и мы передаём её функции. Обратите внимание: функция изменяет значение from , но это изменение не видно снаружи. Функция всегда получает только копию значения:

Значение, передаваемое в качестве параметра функции, также называется аргументом.

  • Параметр – это переменная, указанная в круглых скобках в объявлении функции.
  • Аргумент – это значение, которое передаётся функции при её вызове.

Мы объявляем функции со списком параметров, затем вызываем их, передавая аргументы.

Рассматривая приведённый выше пример, мы могли бы сказать: "функция showMessage объявляется с двумя параметрами, затем вызывается с двумя аргументами: from и "Привет" ".

Значения по умолчанию

Если при вызове функции аргумент не был указан, то его значением становится undefined .

Например, вышеупомянутая функция showMessage(from, text) может быть вызвана с одним аргументом:

Это не приведёт к ошибке. Такой вызов выведет "*Аня*: undefined" . В вызове не указан параметр text , поэтому предполагается, что text === undefined .

Если мы хотим задать параметру text значение по умолчанию, мы должны указать его после = :

Теперь, если параметр text не указан, его значением будет "текст не добавлен"

В данном случае "текст не добавлен" это строка, но на её месте могло бы быть и более сложное выражение, которое бы вычислялось и присваивалось при отсутствии параметра. Например:

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

В приведённом выше примере, функция anotherFunction() не будет вызвана вообще, если указан параметр text .

С другой стороны, функция будет независимо вызываться каждый раз, когда text отсутствует.

Ранние версии JavaScript не поддерживали параметры по умолчанию. Поэтому существуют альтернативные способы, которые могут встречаться в старых скриптах.

Например, явная проверка на undefined :

…Или с помощью оператора || :

Альтернативные параметры по умолчанию

Иногда имеет смысл присваивать значения по умолчанию для параметров не в объявлении функции, а на более позднем этапе.

Во время выполнения функции мы можем проверить, передан ли параметр, сравнив его с undefined :

…Или мы можем использовать оператор || :

Современные движки JavaScript поддерживают оператор нулевого слияния ?? . Его использование будет лучшей практикой, в случае, если большинство ложных значений, таких как 0 , следует расценивать как «нормальные».

Возврат значения

Функция может вернуть результат, который будет передан в вызвавший её код.

Простейшим примером может служить функция сложения двух чисел:

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

Вызовов return может быть несколько, например:

Возможно использовать return и без значения. Это приведёт к немедленному выходу из функции.

В коде выше, если checkAge(age) вернёт false , showMovie не выполнит alert .

Если функция не возвращает значения, это всё равно, как если бы она возвращала undefined :

Пустой return аналогичен return undefined :

Для длинного выражения в return может быть заманчиво разместить его на нескольких отдельных строках, например так:

Код не выполнится, потому что интерпретатор JavaScript подставит точку с запятой после return . Для него это будет выглядеть так:

Таким образом, это фактически стало пустым return .

Если мы хотим, чтобы возвращаемое выражение занимало несколько строк, нужно начать его на той же строке, что и return . Или, хотя бы, поставить там открывающую скобку, вот так:

И тогда всё сработает, как задумано.

Выбор имени функции

Функция – это действие. Поэтому имя функции обычно является глаголом. Оно должно быть кратким, точным и описывать действие функции, чтобы программист, который будет читать код, получил верное представление о том, что делает функция.

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

Например, функции, начинающиеся с "show" обычно что-то показывают.

Функции, начинающиеся с…

  • "get…" – возвращают значение,
  • "calc…" – что-то вычисляют,
  • "create…" – что-то создают,
  • "check…" – что-то проверяют и возвращают логическое значение, и т.д.

Примеры таких имён:

Благодаря префиксам, при первом взгляде на имя функции становится понятным, что делает её код, и какое значение она может возвращать.

Функция должна делать только то, что явно подразумевается её названием. И это должно быть одним действием.

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

Несколько примеров, которые нарушают это правило:

  • getAge – будет плохим выбором, если функция будет выводить alert с возрастом (должна только возвращать его).
  • createForm – будет плохим выбором, если функция будет изменять документ, добавляя форму в него (должна только создавать форму и возвращать её).
  • checkPermission – будет плохим выбором, если функция будет отображать сообщение с текстом доступ разрешён/запрещён (должна только выполнять проверку и возвращать её результат).

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

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

Например, фреймворк jQuery определяет функцию с помощью $ . В библиотеке Lodash основная функция представлена именем _ .

Это исключения. В основном имена функций должны быть в меру краткими и описательными.

Функции == Комментарии

Функции должны быть короткими и делать только что-то одно. Если это что-то большое, имеет смысл разбить функцию на несколько меньших. Иногда следовать этому правилу непросто, но это определённо хорошее правило.

Небольшие функции не только облегчают тестирование и отладку – само существование таких функций выполняет роль хороших комментариев!

Например, сравним ниже две функции showPrimes(n) . Каждая из них выводит простое число до n .

Первый вариант использует метку nextPrime :

Второй вариант использует дополнительную функцию isPrime(n) для проверки на простое:

Второй вариант легче для понимания, не правда ли? Вместо куска кода мы видим название действия ( isPrime ). Иногда разработчики называют такой код самодокументируемым.

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

Итого

Объявление функции имеет вид:

  • Передаваемые значения копируются в параметры функции и становятся локальными переменными.
  • Функции имеют доступ к внешним переменным. Но это работает только изнутри наружу. Код вне функции не имеет доступа к её локальным переменным.
  • Функция может возвращать значение. Если этого не происходит, тогда результат равен undefined .

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

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

  • Имя функции должно понятно и чётко отражать, что она делает. Увидев её вызов в коде, вы должны тут же понимать, что она делает, и что возвращает.
  • Функция – это действие, поэтому её имя обычно является глаголом.
  • Есть много общепринятых префиксов, таких как: create… , show… , get… , check… и т.д. Пользуйтесь ими как подсказками, поясняющими, что делает функция.

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

Задачи

Обязателен ли "else"?

Следующая функция возвращает true , если параметр age больше 18 .

В ином случае она запрашивает подтверждение через confirm и возвращает его результат:

Будет ли эта функция работать как-то иначе, если убрать else ?

Есть ли хоть одно отличие в поведении этого варианта?

Оба варианта функций работают одинаково, отличий нет.

Перепишите функцию, используя оператор ‘?’ или ‘||’

Следующая функция возвращает true , если параметр age больше 18 .

В ином случае она задаёт вопрос confirm и возвращает его результат.

Перепишите функцию, чтобы она делала то же самое, но без if , в одну строку.

Основы JavaScript: функции

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

Определение функции

Определение функции (также известное как объявление, или инструкция функции) всегда начинается с ключевого слова function . Затем следует название этой функции, параметры, которые помещаются в скобки. Дальше идут инструкции JavaScript, которые располагаются в фигурных скобках < >и которые исполняются при вызове функции.

Посмотрим на примере:

Наша функция multiply принимает два аргумента ( a и b ). Внутри фигурных скобок располагается утверждение, которое возвращает результат умножения первого параметра a на второй b .

Функциональное выражение

Есть еще один способ определения функции, а именно, функциональное выражение. Такие типы функций могут быть анонимными. Нет необходимости давать им название.

Например, предыдущую функцию multiply можно записать следующим образом:

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

Еще мы можем определить функцию, основанную на условии. Например, следующая функция addItem сработает, только если num будет равно 1:

Вызов функции

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

Для того, чтобы вызвать нашу функцию multiply необходимо записать:

В данном случае мы вызываем функцию, которая принимает два аргумента со значениями 2 и 2 . Функция исполняется и проходит все инструкции, возвращая значение 4 (2 умноженное на 2).

Функция должна быть в области видимости при вызове, однако определение функции может быть поднято (располагаться ниже вызова в коде), например:

Область видимости функции — это либо та функция, в которой она определена, либо вся программа, если определение функции находится на глобальном уровне.

Обратите внимание: это работает только со стандартной функцией, а не с функциональным выражением.

Область видимости функции

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

Посмотрим на примере:

Вложенные функции и замыкания

Итак, функцию можно вложить в другую функцию! Вложенная (внутренняя) функция является «собственностью» внешней функции. Она также образует замыкание. Замыкание — это выражение (обычно функция), которое может содержать свободные переменные в окружении, связывающем эти переменные («закрывает» выражение).

Так как вложенная функция образует замыкание, можно вызвать внешнюю функцию и задать аргументы как для внешней, так и для внутренней функции.

Замыкания

Как нам уже известно, мы можем «вкладывать» функции, и JavaScript даст вложенной функции полный доступ ко всем переменным и функциям, определенным внутри внешней функции (а также к переменным и функциям, которые для нее доступны).

Однако внешняя функция не имеет доступа к переменным и функциям, определенным внутри вложенной функции! Замыкание создается, когда внутренняя функция каким-то образом становится доступной для области видимости внешней функции.

Посмотрим на примере:

Объект arguments

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

В данном случае i — это первый индекс аргумента, начинающийся с нуля. Поэтому первый аргумент, передаваемый в функцию будет arguments[0] . Общее число аргументов можно получить через arguments.length .

Используя объект arguments , вы можете вызвать функцию с большим количеством аргументов, чем было заявлено при ее определении. Это часто бывает удобно, если вы заранее не знаете, сколько аргументов будет передано функции. Вы можете использовать arguments.length для определения количества аргументов, которое было заявлено изначально, а затем обратиться к каждому из них, используя объект arguments .

Например, рассмотрим функцию, которая соединяет несколько строк. Единственный формальный аргумент для функции — это строка, которая определяет, какими знаками разделять элементы. Функция будет выглядеть следующим образом:

Вы можете передать любое число аргументов в данную функцию, и она соединит каждую строку в новую строку ‘list’:

Обратите внимание: переменная arguments только похожа на массив, но это не так. У нее есть пронумерованный индекс и длина. Однако у нее нет всех методов, присущих массиву.

Параметры функции

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

Параметры по умолчанию

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

Это очень просто воплотить:

Вы можете установить 1 как значение по умолчанию для переменной b в начале функции.

Оставшиеся параметры

Синтаксис оставшихся параметров позволяет передавать неопределенное количество параметров в функцию.

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

Стрелочные функции

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

А вот та же функция, но записанная как стрелочная:

Та функция, но записанная всего в одну строчку! Очень компактно!

Если у нас нет параметров, стрелочная функция записывается следующим образом:

Если же у нас всего один параметр, скобки не обязательны:

И наконец, если вы возвращаете выражение, скобки можно убрать:

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

Предопределенные функции

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

Заключение

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

Руководство по JavaScript, часть 4: функции

Сегодня публикуем четвёртую часть перевода руководства по JavaScript, которая посвящена функциям.

Функции в JavaScript

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

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

Функции в JavaScript являются объектами, если точнее, то они являются объектами типа Function . Их ключевое отличие от обычных объектов, дающее им те исключительные возможности, которыми они обладают, заключается в том, что функции можно вызывать.

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

Сначала рассмотрим особенности работы с функциями и соответствующие синтаксические конструкции, которые существовали в языке до появления стандарта ES6 и актуальны до сих пор.

Вот как выглядит объявление функции (function declaration).

В наши дни такие функции называют «обычными», отличая их от «стрелочных» функций, которые появились в ES6.

Функцию можно назначить переменной или константе. Такая конструкция называется функциональным выражением (function expression).

Можно заметить, что в вышеприведённом примере функция назначена константе, но сама она имени не имеет. Такие функции называют анонимными. Подобным функциям можно назначать имена. В таком случае речь идёт об именованном функциональном выражении (named function expression).

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

В стандарте ES6 появились стрелочные функции (arrow function), которые особенно удобно использовать в виде так называемых «встроенных функций» (inline function) — в роли аргументов, передаваемых другим функциям (коллбэков).

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

Параметры функций

Параметры представляют собой переменные, которые задаются на этапе объявления функции и будут содержать передаваемые ей значения (эти значения называют аргументами). Функции в JavaScript могут либо не иметь параметров, либо иметь один или несколько параметров.

Здесь показано несколько примеров стрелочных функций.

Начиная со стандарта ES6 у функций могут быть так называемые «параметры по умолчанию» (default parameters).

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

В ES8 появилась возможность ставить запятую после последнего аргумента функции (это называется trailing comma). Эта возможность позволяет повысить удобство редактирования кода при использовании систем контроля версий в ходе разработки программ. Подробности об этом можно почитать здесь и здесь.

Передаваемые функциям аргументы можно представлять в виде массивов. Для того чтобы разобрать эти аргументы можно воспользоваться оператором, который выглядит как три точки (это — так называемый «оператор расширения» или «оператор spread»). Вот как это выглядит.

Если функции нужно принимать много параметров, то запомнить порядок их следования может быть непросто. В таких случаях используются объекты с параметрами и возможности по деструктурированию объектов ES6.

Этот приём позволяет, описывая параметры в виде свойств объекта и передавая функции объект, получить в функции доступ к параметрам по их именам без использования дополнительных конструкций. Подробнее об этом приёме можно почитать здесь.

Значения, возвращаемые из функций

Все функции возвращают некое значение. Если команда возврата явно не задана — функция возвратит undefined .

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

Если после ключевого слова return указать некое значение, то это значение возвращается в место вызова функции в качестве результата выполнения этой функции.

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

Конструкцию const [ name, age ] = doSomething() можно прочитать следующим образом: «объявить константы name и age и присвоить им значения элементов массива, который возвратит функция».
Вот как то же самое выглядит с использованием объекта.

Вложенные функции

Функции можно объявлять внутри других функций.

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

Методы объектов

Когда функции используются в качестве свойств объектов, такие функции называют методами объектов.

Ключевое слово this

Если сравнить стрелочные и обычные функции, используемые в качестве методов объектов, можно обнаружить их важное различие, заключающееся в смысле ключевого слова this . Рассмотрим пример.

Как видно, вызов метода start() приводит ко вполне ожидаемому результату, а вот метод stop() явно работает неправильно.

Происходит это из-за того, что ключевое слово this по-разному ведёт себя при его использовании в стрелочных и обычных функциях. А именно, ключевое слово this в стрелочной функции содержит ссылку на контекст, включающий в себя функцию. В данном случае, если речь идёт о браузере, этим контекстом является объект window .

Вот как выглядит выполнение такого кода в консоли браузера.

Особенности ключевого слова this в обычных и стрелочных функциях

Как можно заметить, обращение к this в обычной функции означает обращение к объекту, а this в стрелочной функции указывает на window .

Всё это означает, что стрелочные функции не подходят на роль методов объектов и конструкторов (если попытаться использовать стрелочную функцию в роли конструктора — будет выдана ошибка TypeError ).

Немедленно вызываемые функциональные выражения

Немедленно вызываемое функциональное выражение (Immediately Invoked Function Expression, IIFE) — это функция, которая автоматически вызывается сразу после её объявления.

Точка с запятой перед IIFE необязательна, но её использование позволяет застраховаться от ошибок, связанных с автоматической расстановкой точек с запятой.

В вышеприведённом примере в консоль попадёт слово executed , после чего IIFE завершит работу. IIFE, точно так же как и другие функции, могут возвращать результаты своей работы.

После выполнения этого простого примера в консоль попадёт строка IIFE , которая оказалась в константе something после выполнения немедленно вызываемого функционального выражения. Может показаться, что особой пользы от такой конструкции нет. Однако если в IIFE выполняются некие сложные вычисления, которые нужно выполнить лишь однажды, после чего соответствующие механизмы оказываются ненужными — полезность IIFE оказывается очевидной. А именно, при таком подходе после выполнения IIFE в программе будет доступен лишь возвращённый функцией результат. Кроме того, можно вспомнить, что функции способны возвращать другие функции и объекты. Речь идёт о замыканиях, о них мы поговорим ниже.

Поднятие функций

Перед выполнением JavaScript-кода производится его реорганизация. Мы уже говорили о механизме поднятия (hoisting) переменных, объявленных с использованием ключевого слова var . Похожий механизм действует и при работе с функциями. А именно, речь идёт о том, что объявления функций в ходе обработки кода перед его выполнением перемещаются в верхнюю часть их области видимости. В результате, например, оказывается, что вызвать функцию можно до её объявления.

Если переместить вызов функции так, чтобы он шёл после её объявления, ничего не изменится.

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

В данном случае оказывается, что хотя объявление переменной doSomething и поднимается в верхнюю часть области видимости, это не относится к операции присваивания.
Если вместо var в похожей ситуации использовать ключевые слова let или const , такой код тоже работать не будет, правда, система выдаст другое сообщение об ошибке ( ReferenceError а не TypeError ), так как при использовании let и const объявления переменных и констант не поднимаются.

Стрелочные функции

Сейчас мы подробнее поговорим о стрелочных функциях, с которыми мы уже встречались. Их можно считать одним из наиболее значительных новшеств стандарта ES6, они отличаются от обычных функций не только внешним видом, но и особенностями поведения. В наши дни они используются чрезвычайно широко. Пожалуй, нет ни одного современного проекта, где они не использовались бы в подавляющем большинстве случаев. Можно сказать, что их появление навсегда изменило и внешний вид JS-кода и особенности его работы.

С чисто внешней точки зрения синтаксис объявления стрелочных функций оказывается компактнее синтаксиса обычных функций. Вот объявление обычной функции.

Вот объявление стрелочной функции, которое, в целом, если не учитывать особенности стрелочных функций, аналогично предыдущему.

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

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

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

▍Неявный возврат результатов работы функции

Мы уже касались этой особенности стрелочных функций, но она настолько важна, что её следует обсудить подробнее. Речь идёт о том, что однострочные стрелочные функции поддерживают неявный возврат результатов своей работы. Пример возврата примитивного значения из однострочной стрелочной функции мы уже видели. Как быть, если такая функция должна возвратить объект? В таком случае фигурные скобки объектного литерала могут запутать систему, поэтому в теле функции используются круглые скобки.

▍Ключевое слово this и стрелочные функции

Выше, когда мы рассматривали особенности ключевого слова this , мы сравнивали обычные и стрелочные функции. Этот раздел призван обратить ваше внимание на важность их различий. Ключевое слово this , само по себе, может вызывать определённые сложности, так как оно зависит и от контекста выполнения кода, и от того, включен или нет строгий режим (strict mode).

Как мы уже видели, при использовании ключевого слова this в методе объекта, представленного обычной функцией, this указывает на объект, которому принадлежит метод. В таком случае говорят о привязке ключевого слова this к значению, представляющему собой контекст выполнения функции. В частности, если функция вызвана в виде метода объекта, то ключевое слово this привязано к этому объекту.

В случае же со стрелочными функциями оказывается так, что в них привязка this не выполняется, они пользуются ключевым словом this из содержащих их областей видимости. В результате их не рекомендуется использовать в качестве методов объектов.

Та же самая проблема возникает и при использовании функций в качестве обработчиков событий элементов DOM. Например, HTML-элемент button используют для описания кнопок. Событие click вызывается при щелчке мышью по кнопке. Для того чтобы отреагировать на это событие в коде, нужно сначала получить ссылку на соответствующий элемент, а потом назначить ему обработчик события click в виде функции. В качестве такого обработчика можно использовать и обычную функцию, и стрелочную. Но, если в обработчике событий нужно обращаться к тому элементу, для которого оно вызвано (то есть — к this ), стрелочная функция тут не подойдёт, так как доступное в ней значение this указывает на объект window . Для того чтобы проверить это на практике, создайте HTML-страницу, код которой показан ниже, и понажимайте на кнопки.

В данном случае при нажатии на эти кнопки будут появляться окна, содержащие true . Однако в обработчике события click кнопки с идентификатором fn проверяется равенство this самой кнопке, а в кнопке с идентификатором arrowFn проверяется равенство this и объекта window .

В результате, если в обработчике события HTML-элемента нужно обращаться к this , стрелочная функция для оформления такого обработчика не подойдёт.

Замыкания

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

Когда функция вызывается, у неё есть доступ ко всему тому, что находится во внешней по отношению к ней области видимости. Но к тому, что объявлено внутри функции, извне доступа нет. То есть, если в функции была объявлена некая переменная (или другая функция), они недоступны внешнему коду ни во время выполнения функции, ни после завершения её работы. Однако если из функции возвратить другую функцию, то эта новая функция будет иметь доступ ко всему тому, что было объявлено в исходной функции. При этом всё это будет скрыто от внешнего кода в замыкании.

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

Значение, возвращаемое этой функцией нас пока не интересует, текст выводится в консоль с помощью IIFE, что в данном случае особой роли не играет, однако, это поможет нам увидеть связь между этой функцией и её вариантом, в котором, вместо вызова функции, которая выводит текст в консоль, мы эту функцию из переписанной функции bark() возвратим.

Результат работы код в двух случаях оказывается одинаковым. Но во втором случае то, что было передано исходной функции при её вызове (имя собаки, Roger ), хранится в замыкании, после чего используется другой функцией, возвращённой из исходной.

Проведём ещё один эксперимент — создадим, пользуясь исходной функцией, две новых, для разных собак.

Этот код выведет следующее.

Оказывается, что значение константы say привязано к функции, которая возвращена из функции prepareBark() .

Обратите внимание на то, что say , при повторном вызове prepareBark() , получает новое значение, при этом значение, записанное в say при первом вызове prepareBark() , не меняется. Речь идёт о том, что при каждом вызове этой функции создаётся новое замыкание.

Итоги

Сегодня мы говорили об обычных и стрелочных функциях, об особенностях их объявления и использования, о том, как, в разных ситуациях ведёт себя ключевое слово this , и о замыканиях. В следующий раз обсудим массивы и циклы.

Уважаемые читатели! Как вы относитесь к стрелочным функциям в JavaScript?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *