Обычно определение функции отделяется от ее вызова: сначала мы определяем функцию, а потом вызываем. Но это необязательно. Мы также можем создать такие функции, которые будут вызываться сразу при определении. Такие функции еще называют Immediately Invoked Function Expression или IIFE. Подобные функции заключаются в скобки, и после определения функции идет в скобках передача параметров:
// самозывающаяся функция (function(){ console.log("Привет мир"); }());
Здесь всю это небольшую программу можно разбить на несколько частей. В скобках идет определение анонимной функции:
function(){ console.log("Привет мир"); }
Это обычная функция, которая выводит некоторую строку. Но также в скобках после определения функции идут пустые скобки:
function(){ console.log("Привет мир"); }()
Эти скобки представляют те же скобки, которые идут при вызове функции и в которые помещаются значения для параметров функции. Однако наша анонимная функция не имеет параметров, поэтому и скобки пустые. То есть здесь фактически идет вызов функции сразу после ее определения. И вся эта конструкция обертывается в скобки.
В итоге мы определяем анонимную функцию и сразу же ее вызываем.
Подобным образом можно создать и вызывать функцию, которые принимают параметры:
(function (a, b){ const result = a + b; console.log(`${a} + ${b} = ${result}`); }(4, 5));
Здесь функция принимает два параметра, значения которых складываются, а результат выводится на консоль. И сразу после определения функции мы передаем ее параметрам два числа - 4 и 5. То есть при выполнении программы будет определяться и сразу же выполняться функция, которая подсчитает сумму чисел 4 и 5.
Замыкания и IIFE-функции упрощают написание программ в функциональном стиле. Так, рассмотрим следующий пример:
console.log( ((x,y) => ( ((proc2) =>( ((proc1)=>proc1(5,30))((x,y) => [x, proc2(), y]) ))(()=>x + y) ))(10, 15) ); // [5, 25, 30]
Рассмотрим, что здесь делается. Прежде всего в функции console.log()
выводится результат самовызывающейся функции:
((x,y) => ( ((proc2) =>( ((proc1)=>proc1(5,30))((x,y) => [x, proc2(), y]) ))(()=>x + y) ))(10, 15)
Эта первая IIFE-функция принимает параметры x и y, которым передаются числа 10 и 15.
Эта функция, в свою очередь использует вложенную функцию и возвращает ее результат:
((proc2) =>( ((proc1) => proc1(5,30))((x,y) => [x, proc2(), y]) ))(()=>x + y)
Эта вторая IIFE-функция. Она в качестве параметра proc2 принимет другую функцию. В данном случае это функция ()=>x + y
, которая возвращает сумму значений. Поскольку для вложенной функции лексическое окружение
представлено параметрами x и y внешней функции, то функция-параметр proc2 получает эти значения и возвращает их сумму - 10 + 15=25.
Однако вложенная функция возвращает результат своей вложенной функции:
((proc1) => proc1(5,30))((x,y) => [x, proc2(), y])
Эта третья функция также является IIFE-функцией. В качестве параметр proc1 она принимает также некоторую функцию-коллбек. И в эту функцию proc1 передаются числа 5, 30. И тут идет наиболее сложный момент - далее в скобках передается сама функция proc1:
(x,y) => [x, proc2(), y]
Она определяет параметры x и y и возвращает в качестве результата массив из трех чисел. Прежде всего первое и третье числа - х и y. Эти параметры не стоит путать с параметрами х и y первой IIFE-функции самого верхнего уровня:
((x,y) => (.....))(10, 15)
Итак, самая третья функция возвращает массив [x, proc2(), y]
. x и y представляют параметры этой функции. Второе значение в массиве представляет результат вызова функции proc2. Вспомним, что это за функция,
и для этого поднимемся на уровень выше ко второй функции:
((proc2) =>( ................. ))(()=>x + y)
proc2 возвращает сумму значений x и y. Для этой второй функции лексическое окружение определяется первой IIFE-функцией, которая принимает параметры x и y, поэтому в proc2 передаются числа 10 и 15,
соответственно функция возвращает 25. И, таким образом, результат будет представлять массив [5, 25, 30]
.