Функции в JavaScript

Функции в JavaScript

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

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

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

Функция:

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

Процедура - это функция, которая возвращает пустое значение (при этом выполнение кода процедуры непосредственно сказывается на выполнении программы). 

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

Важно отличать вызов функции от вызова метода:

  • для вызова метода необходим объект, которому принадлежит вызываемый метод (object.functionProperty() или object['functionProperty']()), а для вызова функции — нет (expression()).
Замечание

В JS любая процедура на самом деле является функцией без return: если опустить return, функция всё равно неявно возвращает undefined и остаётся функцией.

[свернуть]

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

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

[свернуть]

Хорошим тоном в программировании является правило: одна функция – одно действие.

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

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

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

  1. в переменной:
  2. в объекте:
  3. в массиве:

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

  1. "function declaration";
  2. "function expression".

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

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

где 

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

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

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

[свернуть]

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

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

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

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

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

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

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

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

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

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

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

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

[свернуть]

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

Существует ещё один синтаксис создания функций, который называется Function Expression (Функциональное Выражение). В данном случае ключевое слово function может использоваться для определения функции внутри выражения (при этом функция может являться анонимной, т.е. может не иметь собственного именования).

Синтаксис:

где

  • name - имя функции, является локальным для её тела и может быть опущено (в таком случае функция является анонимной);
  • paramN - имя аргумента, передаваемого в функцию;
  • statements - инструкции (программный код), составляющие тело функции.

Особенности использования функциональных выражений:

  1. Если необходимо сослаться на текущую функцию внутри тела этой функции, то нужно создать именованное функциональное выражение. Имя будет локальным (только для тела функции, её области видимости).
  2. Переменная, которой присвоено функциональное выражение, будет иметь свойство name, содержащее имя функции, и которое не изменяется при переприсваивании другой переменной:
    • для анонимной функции значением свойства name будет имя переменной (неявное имя);
    • если имя функции задано, то будет использовано имя функции (явное имя).
  3. При объявлении функции синтаксисом "function expression" "поднятие" или "всплытие" функции JavaScript (hoisting) не работает.
Примеры

Отсюда...

Пример использования ссылки на текущую функцию внутри тела этой функции:

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

Пример ошибки при "поднятии (всплытии)":

[свернуть]

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

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

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

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

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

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

Синтаксис "оставшиеся параметры"

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

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

Отличия оставшихся параметров от объекта arguments:

  1. оставшиеся параметры включают только те, которым не задано отдельное имя, в то время как объект arguments содержит все аргументы, передаваемые в функцию;
  2. объект arguments не является массивом, в то время как оставшиеся параметры являются экземпляром Array и методы sort, map, forEach или pop могут непосредственно у них использоваться;
  3. объект arguments имеет дополнительную функциональность, специфичную только для него (например, свойство callee).

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

[свернуть]

Особенности использования параметров (аргументов) функций JavaScript:

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

Особенности использования аргументов различных типов:

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

Отсюда...

[свернуть]

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

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

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

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

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

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

В параметрах по умолчанию можно использовать значения предыдущих (расположенных левее в списке) параметров.

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

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

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

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

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

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

ВАЖНО! Никогда не называйте параметр arguments: он будет иметь приоритет над объектом arguments, который доступен для каждой функции.

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

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

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

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

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

[свернуть]

Как и с обычными массивоподобными объектами для преобразования объекта arguments в обычный массив можно использовать метод Array.from() или оператор расширения ( ... ). Если последний именованный аргумент функции имеет префикс ..., он автоматически становится массивом с элементами от 0 до arguments .length в соответствии с актуальным количеством аргументов, переданных в функцию:

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

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

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

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

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

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

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

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

[свернуть]

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

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

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

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

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

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

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

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

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

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

[свернуть]

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

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

Пример

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

Функция перебора элементов массива:

[свернуть]

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

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

Подробнее...

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

function foo(){ .. } ()

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

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

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

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

( function(){ .. } () )

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

 

Возврат значения функции 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().

 

Один комментарий к “Функции в JavaScript”

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

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