Функции в JavaScript

Функции в JavaScript

Функция (в общем случае) — это отношение между элементами, при котором изменение в одном элементе влечёт изменение в другом.

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

Функции позволяют не повторять один и тот же код во многих местах программы и являются основными «строительными блоками» программ. Языки программирования позволяют программисту использовать как встроенные функции (например, alert(message), prompt(message, default),  confirm(question)), так  и создавать свои.

Функция:

  • должна быть соответствующим образом объявлена и определена;
  • может вызываться и принимать параметры;
  • определяет собственную (локальную) область видимости, куда входят:
    1. входные параметры, а также
    2. те переменные, которые объявляются непосредственно в теле самой функции;
  • должна возвращать некоторое значение (возможно пустое или другую функцию).

Процедура — это функция, которая возвращает пустое значение. 

Метод — это функция, которая является свойством объекта.

В JavaScript любая функция — это объект, и следовательно, ею можно манипулировать как объектом, в частности:

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

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

Хранение функций в JS

В JavaScript функции могут храниться тремя способами:

  1. в переменной: let fn = function doSomething() {};
  2. в объекте: let obj = { doSomething : function(){} };
  3. в массиве: arr.push(function doSomething() {}).

Объявлять функции в JavaScript можно с помощью синтаксиса:

  1. «function declaration»;
  2. «function expression».

Объявление функции (Function Declaration)

Для создания функций используется её объявление, например:

где 

  • function — ключевое слово;
  • имя — имя функции;
  • (параметры) —  список параметров в круглых скобках через запятую (может быть пустым) ;
  • {…тело…} — инструкции или исполняемый код функции (определение функции или «тело функции»), внутри фигурных скобок.

Разбор примера

  • function — ключевое слово;
  • sayHello — имя функции;
  • (firstName, lastName) —  список параметров в круглых скобках через запятую (может быть пустым) ;
  • {console.log(Hello, ${firstName} ${lastName});} — код функции («тело функции»), внутри фигурных скобок;
  • sayHello(«Alex», «NAV») — вызов функции с передачей параметров (аргументов).

[свернуть]

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

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

Объявление функции (Function Expression)

Существует ещё один синтаксис создания функций, который называется Function Expression (Функциональное Выражение):

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

Подробнее…

Когда функция (как выражение) обернута в пару ( ), мы можем вызвать эту функцию, добавив еще одни () в конце, например:

( function foo(){ .. } ) ()

Первая окружающая пара ( ) делает функцию выражением, а вторая () — выполняет функцию.

Этот шаблон настолько в ходу, что существует термин для его обозначения: IIFE (Immediately (немедленно) Invoked (вызываемое) Function (функциональное) Expression (выражение)).

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

Другой вариант традиционной формы IIFE:

( function(){ .. } () )

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

[свернуть]

ВАЖНО!

При объявлении функции синтаксисом «function expression» «поднятие» или «всплытие» функции JavaScript (hoisting) не работает.

[свернуть]

Function Expression использует внутри себя инструкции присваивания let funcName = …; как значение. Это не блок кода, а выражение с присваиванием. Таким образом, точка с запятой после фигурных скобок {}; не относится непосредственно к Function Expression, она лишь завершает инструкцию.

В JavaScript функции – это значения, поэтому мы и обращаемся с ними, как со значениями. 

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

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

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

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

Например, функции, начинающиеся с :

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

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

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

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

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

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

[свернуть]

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

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

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

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

  • функция, в которой она определена, или
  • целая программа, если она объявлена по уровню выше.

Функции JS расположены  в области видимости, если они уже определены выше по коду, но функции вида «function declaration» могут быть подняты («поднятие» или «всплытие» — hoisting), также как в примере выше (вызов функции расположен в коде выше, чем её объявление).

ВАЖНОЕ замечание ("поднятие" или "всплытие" функции JavaScript (hoisting))

«Поднятие» или «всплытие» функции JavaScript (hoisting) работает только тогда, когда объявлении функции использует синтаксис function funcName(){} («function declaration»).

При объявлении функции синтаксисом «function expression» «поднятие» или «всплытие» функции JavaScript (hoisting) не работает.

Код ниже работать не будет!

[свернуть]

Рекурсивный вызов функции

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

Пример

Функция рекурсивного вычисления факториала:

[свернуть]

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

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

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

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

В примере выше в функцию sayHello передаются два параметра: firstName и lastName. Когда функция вызывается, переданные значения копируются в локальные переменные firstName и lastName, которые затем используются в теле функции.

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

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

Аргументами функции могут быть не только строки и числа, но и целые объекты.

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

См. также ES6 значения по умолчанию: базовый пример

Если параметр не указан, то его значением становится undefined:

В вызове не указан параметр lastName, поэтому предполагается что lastName=== undefined.

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

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

Использование объекта arguments функции

Внутри функции вы можете ссылаться к аргументам следующим образом:

где i — это порядковый номер аргумента, отсчитывающийся с 0.

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

Объект arguments функции является псевдо-массивом, в котором есть пронумерованные индексы и свойство length, к нему применимы некоторые методы массивов, например, for .. in. Однако он не обладает всеми методами массивов.

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

Примеры использования объекта arguments

Функция, которая конкатенирует несколько строк:

Использование for … in для объекта arguments

[свернуть]

Переменные в функциях JavaScript

  1. Локальные переменные — это переменные, объявленные внутри функции, видны только внутри этой функции. Код вне функции не имеет доступа к её локальным переменным.
  2. Внешние переменные — это переменные, объявленные вне функции (например, во внешней функции при вложенности функций). Функция обладает полным доступом к внешним переменным и может изменять их значение.
  3. Глобальные переменные — это переменные, объявленные снаружи всех функций. Глобальные переменные видимы для любой функции (если только их не перекрывают одноимённые локальные переменные).

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

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

Алгоритм использования переменных в функции:

  1. Ищется локальная переменная (если она объявлена внутри тела функции).
  2. Внешняя переменная используется, только если внутри функции нет такой локальной.
  3. Если одноимённая переменная объявляется внутри функции, тогда она перекрывает внешнюю.
Примеры

Функция использует только локальную переменную:

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

[свернуть]

Возврат значения функции JS

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

Функция возвращает другую функцию

Разберем исполнение этого кода:

  1. Строка 1. Мы объявляем переменную val в глобальном контексте выполнения и присваиваем число 7 этой переменной.
  2. Строки 2-8. Мы объявляем переменную с именем createAdder в глобальном контексте выполнения и назначаем ей определение функции. Строки 3-7 описывают указанное определение функции. Но на данный момент тело этой функции не выполняется. Мы просто сохраняем определение функции в этой переменной (createAdder).
  3. Строка 9. Мы объявляем новую переменную с именем adder в глобальном контексте выполнения. Временно переменной adder присваивается undefined.
  4. Строка 9. Скобки () указывают, что нужно выполнить или вызвать функцию с именем createAdder, которая была создана на шаге 2:
    1. вызов функции createAdder (теперь мы находимся в строке 2);
    2. создается новый локальный контекст выполнения, мы можем создавать локальные переменные в новом контексте выполнения;
    3. движок добавляет новый контекст в стек вызовов. Функция createAdder не имеет аргументов, перейдем прямо к ее телу.
  5. Строки 3-6. У нас есть новое объявление функции. Мы создаем переменную addNumbers в локальном контексте выполнения (это важно, addNumbers существует только в локальном контексте выполнения). Мы сохраняем определение функции в локальной переменной с именем addNumbers, и переходим к строке 7.
  6. Строка 7. Мы возвращаем содержимое переменной addNumbers. Движок ищет переменную с именем addNumbers и находит ее: это определение функции. Таким образом, далее:
    1. возвращается (return addNumbers;) определение функции addNumbers, т.е. всё что находится в скобках в строках 4 и 5;
    2. удаляется локальный контекст выполнения из стека вызовов (переменная addNumbers больше не существует;
    3. определение функции addNumbers все еще существует, оно возвращается из функции и присваивается переменной adder (это переменная, которую мы создали на шаге 3).
  7. Строка 10. Мы определяем новую переменную sum в глобальном контексте выполнения. Временное значение undefined. Далее нужно выполнить функцию, которая определена в переменной с именем adder и которая принимает два параметра:
    1. переменная val, которую мы определили на шаге 1, она представляет число 7;
    2. число 8.
  8. Теперь мы должны выполнить эту функцию. Определение функции обозначено строками 3-5. Новый локальный контекст выполнения создан. В локальном контексте создаются две новые переменные: a и b. Им соответственно присваиваются значения 7 и 8, так как это были аргументы, которые мы передали функции на предыдущем шаге.
  9. Строка 4. В локальном контексте выполнения объявлена ​​новая переменная ret.
  10. Строка 4. Выполняется сложение содержимого переменной a и содержимого переменной b. Результат сложения (15) присваивается переменной ret.
  11. Переменная ret возвращается из этой функции. Локальный контекст выполнения уничтожается, он удаляется из стека вызовов, переменные a, b и ret больше не существуют. Возвращаемое значение присваивается переменной суммы, которую мы определили на шаге 9.
  12. Мы выводим значение суммы на консоль.

[свернуть]

Ещё примеры с разбором…

Особенности директивы return:

  1. Директива return может находиться в любом месте тела функции.
  2. Код в теле функции после директивы return не выполняется.
  3. Как только выполнение доходит до этого места, функция останавливается, и значение возвращается в вызвавший её код.
  4. Возможно использовать return и без значения, что приведёт к немедленному выходу из функции.
  5. Результат функции с пустым return или без него – undefined.
  6. Вызовов return может быть несколько.
Примеры

Несколько вызовов директивы return:

[свернуть]

Никогда не добавляйте перевод строки между return и его значением!

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

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

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

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

Свойства функций

Функция — это объект, поэтому она может иметь свои собственные свойства, например name, length или методы toString(), bind(), apply() и call().

 

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.