Зачем нужны анонимные функции
Перейти к содержимому

Зачем нужны анонимные функции

  • автор:

Зачем нужны анонимные функции

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

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

$hello = function($name) < echo "

Hello $name

"; >;

Здесь переменной $hello присваивается анонимная функция. Эта функция также определяется с помощью ключевого слова function . Она также принимает параметры — в данном случае параметр $name . И также она имеет некоторый блок операторов.

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

$hello("Tom");

Фактически подобная переменная применяется как стандартная функция.

Hello $name"; >; $hello("Tom"); $hello("Bob"); ?>

Анонимные функции в PHP

Также анонимные функции могут возвращать некоторое значение:

; $number = $sum(5, 11); //16 echo $number; ?>

Распространенным случаем применения анонимных функций является передача их параметрам других функции. Таким анонимные функции еще называют функциями обратного вызова или коллбеками (callback function). Рассмотрим простейший пример:

 welcome(function() < echo "Hello!"; >); ?>

В данном случае функция welcome() имеет параметр $message , который внутри функции вызывается подобно функции $message() .

При вызове функции welcome() параметру $message передается анонимная функция, которая выводит строку «Hello!». В этоге при вызове

$message();

Фактически будет выполняться функция

function()

Подобным образом мы можем передавать одну функцию различные анонимные функции:

 $goodMorning = function() < echo "

Доброе утро

"; >; $goodEvening = function() < echo "

Добрый вечер

"; >; welcome($goodMorning); // Доброе утро welcome($goodEvening); // Добрый вечер welcome(function()< echo "

Привет

"; >); // Привет ?>

Анонимные функции callback в PHP

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

function sum($numbers) < $result = 0; foreach($numbers as $number)< $result += $number; >return $result; > $myNumbers = [-2, -1, 0, 1, 2, 3, 4, 5]; $numbersSum = sum($myNumbers); echo $numbersSum; // 12

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

 > return $result; > // для четных чисел $isEvenNumber = function($n)< return $n % 2 === 0;>; // для положительных чисел $isPositiveNumber = function($n) < return $n >0;>; $myNumbers = [-2, -1, 0, 1, 2, 3, 4, 5]; $positiveSum = sum($myNumbers, $isPositiveNumber); $evenSum = sum($myNumbers, $isEvenNumber); echo "Сумма положительных чисел: $positiveSum 
Сумма четных чисел: $evenSum"; ?>
Сумма положительных чисел: 15 Сумма четных чисел: 4

Здесь функция sum() в качестве второго параметра — $condition принимает другую функцию. В цикле при переборе массива с помощью этой функции мы проверяем, удовлетворяет ли элемент массива условию:

if($condition($number))

Причем сейчас мы не значем, что это будет за условие, как именно функция $condition будет работать. Мы только знаем, что она получает число и возвращает true , если число удовлетворяет условию, либо false — если не удовлетворяет.

И если только число соответствует условию, тогда прибавляем число к общему результату:

$result += $number;

При вызове функции sum() в качестве параметра $condition передаются конкретные анонимные функции. Например, функиция, которая определяет, является ли число четным:

$isEvenNumber = function($n)< return $n % 2 === 0;>;

Логика функции проста: если остаток от деления числа на 2 равен 0, то число четное. и функция возвращает true .

Далее мы передаем эту функицию:

$evenSum = sum($myNumbers, $isEvenNumber);

В итоге при вызове if($condition($number)) фактически будет выполняться функция $isEvenNumber() .

Анонимные функции в JavaScript

Анонимной в JavaScript называют функцию с которой не связано никакое имя или другими словами у такой функции нет имени. Также можно сказать, что если после ключевого слова function или перед знаком стрелочной функции => не стоит имя — функция анонимная. Однако если такую функцию положить в переменную она уже считается именованной.

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

Самовызывающаяся функция

 (function() < alert('Я буду настойчив и выучу все возможные записи функций!'); >)(); 

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

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

Анонимные функции в качестве параметров

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

 setTimeout(function() < alert('Время заняться практикой JavaScript!'); >, 5000); 

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

Еще один пример, но уже не с кастомной функцией.

 function importantQuestion(question, yes, no) < if (confirm(question)) yes() else no(); >importantQuestion( "Вы хотите изучать JavaScript?", function() < alert("Не опускайте руки и у вас все получиться!"); >, function() < alert("Отличная новость! Чем меньше у меня конкурентов, тем выше моя зарплата."); >); 

Итого

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

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

3. Анонимные функции могут вызывать сами себя.

4. Анонимные функции могут выступать в качестве параметров в других функциях.

Skypro — научим с нуля

Что такое анонимная функция в JavaScript?

Два наиболее распространенных способа создания функции в Javascript — это использование объявления функции или оператора функции. Анонимные функции создаются с помощью оператора функции. Анонимные функции сильно используются в JavaScript для многих вещей, в частности многих обратных вызовов, используемых многими фреймворками языка. Спецификация ECMAScript не упоминает термин анонимный.

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

Определение нормальной функции:

function callMe() < alert('Hello, I am normal function !!'); >callMe();

В приведенном выше скрипте вы видите, что он создает функцию с именем «callMe».

Определение анонимной функции:

var callMe = function() < alert('Hello, I am Anonymous !!'); >callMe();

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

Здесь мы видим, что эти два способа определения функции по существу одинаковы; оба приводят к создаваемой функции и новую переменную с именем «callMe», назначенную для текущей области. Однако вторая функция анонимна. Функциональный оператор можно использовать где угодно, чтобы использовать выражение. Например, вы можете использовать оператор функции, когда назначается переменная, когда параметр передается функции или в операторе return.

Анонимные функции в JavaScript

Разбираемся в том, что является анонимной функцией или какая связь между анонимными функциями в JavaScript и подбрасыванием монетки? Может ли функция с именем быть анонимной?

tl;tr;

JavaScript-разработчик, запомни! Если после function есть имя — функция именованная, во всех остальных случая анонимная.

В интернете кто-то не прав

Все началось с простого вопроса в канале Telegram для изучающих JavaScript, в котором, помимо всего прочего, появился вопрос касательно обработчиков событий в браузере. Вопрос был в том, как они «навешиваются» и «снимаются» с DOM-элемента. Среди ответов от вполне опытного разработчика был следующий:

Обработчик снимается также, как он вешался. Если вешалась анонимная функция, то никак. Только удалять элемент из DOM и из памяти.

Далее ответ был подкреплен блоком кода похожим на этот:

element.addEventListener('click', function() < /* .. */ >)
element.removeEventListener('click', function() < /* .. */ >)

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

const handleClick = () => < /* */ >element.addEventListener('click', handleClick)
element.removeEventListener('click', handleClick)

В twitter как на работу

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

И для этого было вполне достаточно спросить насчёт небольшого блока кода:

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

Также задавал этот вопрос в личной беседе опытным JavaScript-разработчикам, выступающим на митапах с докладами, и людям не из мира фронтенда, результат развед-опроса был сильно похож на статистику ответов в twitter.

Я знал ответ, это же просто

Если на опрос выше вы ответили, что функция myFunc является анонимной, поздравляю—это правильно! Отметьте этот день красным цветом в календаре, позовите родных и близких, начинайте разливать шампанское по бокалам.

Итак, значит функция в коде блока выше является анонимной:

const myFunc = function() < >;

А что, если я тебе скажу, что ты можешь обратится к свойству name, и получить конкретное значение?

myFunc.name // "myFunc"

Полученное значение не пустое, но при этом функция выше, как мы выяснили, анонимная, но это же какой-то понятийный коллапс, товарищи! Не торопитесь уходить в backend-разработку, Шерлок Холмс уже начал свое расследование.

Как задать функцию в JavaScript

new Function ([arg1[, arg2[, . argN]],] functionBody)
function name([param[, param,[. param]]]) [statements]
>
const myFunc = function [name]([param1[, param2[, . paramN]]]) statements
>
(param1, param2, …, paramN) => < statements >
(param1, param2, …, paramN) => expression
const obj = property( parameters… ) <>, 
[property]( parameters… ) <>,
>;

К тому же, не стоит забывать о существовании Generator Function и об специальном синтаксисе с использованием async, плюс вспомним о setters, getters. Все это добавляет вариации в вышеуказанные способы определения функций.

Усиливаем пример с определением имени. Тесты

const myFunc = function() < >; // 1 
const myFuncA = function myFuncA2() < >; // 2
const myFuncB = () => < >; // 3
const myFuncC = new Function(); // 4
const property = Symbol('symbolProperty');const myObject = <
methodA: function() < >, // 5
methodB: function MyMethodB() <>, // 6
methodC: () => < >, // 7
methodD()< >, // 8
[property]() < >// 9
>;
function myFuncD() < >; // 10
(function() < >)(); // 11
(function myFuncF()< >)(); // 12

Теперь, используя описанные способы выше, возьмём у каждой функции свойство name, и начнем с функций, заданных как Function expression:

myFunc.name // "myFunc"
myFuncA.name // "myFuncA2"
myFuncB.name // "myFuncB"
myFuncC.name // "myFuncC"

Функции, объявленные как Object Method:

myObject.methodA.name // "methodA"
myObject.methodB.name // "MyMethodB"
myObject.methodC.name // "methodC"
myObject.methodD.name // "methodD"
myObject[property].name // "[symbolProperty]"
(function myFuncD() < >).name // "myFuncD"
(function() < >).name // ""
(function myFuncF()< >).name // "myFuncF"
(new Function()).name // "anonymous"

Вопросов возникает ещё больше. А может функция в опросе из twitter все таки именованная? Может я ввел вас в заблуждение?

Спецификация. Хардкор

Возможно кто-то считает, что когда-то трава была зеленее, небо голубее и вода мокрее, но в мире JavaScript раньше было определенно хуже! И только начиная со стандарта ECMA-262 6th Edition появилась определенность в понятии анонимной функции, в частности в главе 14 ECMAScript Language: Functions and Classes, посвященной функциям и классам, в пункте 14.1.9 IsAnonymousFunctionDefinition:

14.1.9 Static Semantics: IsAnonymousFunctionDefinition( production )1. If IsFunctionDefinition of production is false, return false.
2. Let hasName be the result of HasName of production.
3. If hasName is true, return false.
4. Return true

Откуда получаем, что для полного понимания придется разобраться в семантике IsFunctionDefinition:

14.1 Function Definitions
14.1.11 Static Semantics: IsFunctionDefinition
FunctionExpression:
function ( FormalParameters )
1. Return true.
function BindingIdentifier ( FormalParameters )
1. Return true.

А также разобраться в семантике hasName:


14.1 Function Definitions
14.1.8 Static Semantics: HasName
FunctionExpression:
function ( FormalParameters ) < FunctionBody >
1. Return false.
function BindingIdentifier ( FormalParameters ) < FunctionBody >
1. Return true.

Из семантики hasName для Arrow Function следует, что стрелочные функции всегда анонимны:

14.2 Arrow Function Definitions
14.2.7 Static Semantics: HasName
ArrowFunction:
ArrowParameters => ConciseBody

1. Return false.

С описанием метода объекта дела обстоят несколько сложнее, поскольку может использоваться старый синтаксис через AssignmentExpression или новый через MethodDefinition:

12.2.6 Object Initializer 
..
PropertyDefinition :
..
PropertyName : AssignmentExpression
MethodDefinition

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

const myObject = < 
myMethodA: function() <>, // 1
myMethodB() <> // 2
>;
myObject.myMethodB = function() <>; // 3

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

Что же стоит запомнить? Все случаи с описанием методов сводятся к Function Expression, где свойству объекта задается в качестве значения функция.

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

Возвращаемся к тестам и ставим точку.

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

const myFunc = function() < >; // 1 - анонимая 
const myFuncA = function myFuncA2() < >; // 2 - именованная
const myFuncB = () => < >; // 3 - анонимая
const myFuncC = new Function() // 4 - анонимая
const property = Symbol('symbolProperty')
const myObject = <
methodA: function() < >, // 5 - анонимная
methodB: function MyMethodB() <>, // 6 - именованная
methodC: () => < >, // 7 - анонимная
methodD()< >, // 8 - анонимная
[property]() < >// 9 - анонимная
>
function myFuncD() < >; // 10 - именованная
(function() < >)() // 11 - анонимая
(function myFuncF()< >)() // 12 - именованная

Стойте! А как же name?

Не стоит забывать, что JavaScript мультипарадигмальный язык программирования, где есть элементы объектно-ориентированного подхода, где функцию можно рассматривать как объект со своими свойствами. К таким свойствам относится свойство name, и в спецификации легко обнаружить (нет) описание SetFunctionName в 9.2. ECMAScript Function Objects:

9.2 ECMAScript Function Objects
9.2.11 SetFunctionName (F, name, prefix)
The abstract operation SetFunctionName requires a Function argument F, a String or Symbol argument name and optionally a String argument prefix. This operation adds a name property to F by performing the following steps:

..
6. Return DefinePropertyOrThrow(F, "name", PropertyDescriptor [[Value]]: name,
[[Writable]]: false,
[[Enumerable]]: false,
[[Configurable]]: true
>).
..

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

Например, для методов именем функции будет являться имя свойства, согласно 14.3.8 Runtime Semantics: DefineMethod и 14.3.9 Runtime Semantics: PropertyDefinitionEvaluation, а для функций созданных с помощью конструктора Function, именем функции будет “anonymous”:

19.2.1.1.1 RuntimeSemantics: CreateDynamicFunction(constructor, newTarget, kind, args)

..
29. Perform SetFunctionName(F, "anonymous").
..

И лично для себя открыл в спецификации то, как задается имя для функции, привязанной к контексту с помощью метода bind, что описано в разделе 19.2.3.2 Function.prototype.bind, например:

const myFunc = function() console.log(this.myProperty)
>
const myBind = myFunc.bind(< myProperty: 'value' >)
console.log(myBind.name); // "bound myFunc"

Также подобные префиксы в именах имеют функции, созданные как getter-метод объекта или setter-метод объекта, согласно 14.3.9 Runtime Semantics: PropertyDefinitionEvaluation, например:

const myObject = get myProperty() < return 42; >
>
console.log(
Object.getOwnPropertyDescriptor(myObject, "myProperty").get.name
) // "get myProperty"

AST-ановитесь!

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

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

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

Вывод

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

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

Изучайте JavaScript! JavaScript — во имя добра!

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

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