Вызов функции на месте в JavaScript

Сейчас мы разберем прием, который позволяет вызвать функцию прямо на месте ее объявления. Такая конструкция называется Immediately Invoked Function Expression (IIFE).

Давайте посмотрим на примере. Пусть у нас есть вот такое функциональное выражение:

let func = function() { alert('!'); }; func(); // выведет '!'

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

+function() { alert('!'); // выведет '!' }();

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

Определите, не запуская код, что выведется на экран:

!function() { alert('!'); }();

Определите, не запуская код, что выведется на экран:

function() { alert('!'); }();

Присваивание в переменную

Пусть теперь наша вызываемая на месте функция не выводит что-то алертом на экран, а возвращает через return:

+function() { return '!'; }();

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

let result = function() { return '!'; }(); alert(result); // выведет '!'

Так как в данном случае идет присваивание в переменную, то плюс уже не нужен.

Определите, не запуская код, что выведется на экран:

let result = function() { return '!'; }(); alert(result);

Определите, не запуская код, что выведется на экран:

let result = function() { return '!'; }; alert(result);

Определите, не запуская код, что выведется на экран:

let result = function() { return '!'; }; alert(result());

Применение вызова на месте

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

let sum = 1 + function() { return 2; }(); alert(sum); // выведет 3

Определите, не запуская код, что выведется на экран:

let result = function() {return 1;}() + function() {return 2;}(); alert(result);

Круглые скобки

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

(function() { alert('!'); }());

Чаще всего круглые скобки вызова функции ставят снаружи, вот так:

(function() { alert('!'); })();

Определите, не запуская код, что выведется на экран:

let result = (function() { return '!'; }()); alert(result);

Определите, не запуская код, что выведется на экран:

let result = (function() { return '!'; })(); alert(result);

Определите, не запуская код, что выведется на экран:

let result = (function() { return '!'; }); alert(result);

Определите, не запуская код, что выведется на экран:

let result = (function() { return '!'; }); alert(result());

Параметры

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

function(str) { alert(str); }

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

(function(str) { alert(str); // выведет '!!!' })('!!!');

Определите, не запуская код, что выведется на экран:

(function(num1, num2) { alert(num1 + num2); })(1, 2);

Несколько скобок

Пусть вам предложат хитрую задачу: сделать несколько вызывающих скобок, вот так:

(function() { // какой-то код })()(); // несколько вызывающих скобок

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

(function() { return function() { alert('!'); }; })()(); // выведет '!'

Допишите следующий код так, чтобы его запуск алертом выводил '!':

(function() { // какой-то код })()()();

Допишите следующий код так, чтобы его запуск выводил сумму переданных параметрами чисел:

(function() { // какой-то код })(1)(2);

Допишите следующий код так, чтобы его запуск выводил сумму переданных параметрами чисел:

(function() { // какой-то код })(1)(2)(3);

Подводные камни

Давайте рассмотрим два кусочка кода.

Первый:

let result = 1 +function() { return 2; }(); alert(result);

Второй:

let result = 1; +function() { return 2; }(); alert(result);

Эти два кусочка кода практически одинаковы, но если их запустить - результат будет отличаться. Первый код выведет на экран число 3, а второй - число 1.

Почему получилась такая разница: все дело в том, что в одном случае в первой строке кода в конце отсутствует точка с запятой, а во втором случае - присутствует.

Вы можете спросить: как же так, ведь в JavaScript точка с запятой в конце команды не является обязательной! На самом деле это не совсем так. Давайте разберемся, что у нас на самом деле происходит.

Первый код можно переписать вот так:

let result = 1 + function() { return 2; }(); alert(result); // выведет 3

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

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

// Первая команда: let result = 1; // Вторая команда: +function() { return 2; }(); // Третья команда: alert(result); // выведет 1

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

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

Давайте тогда разберемся, а почему мы вообще можем не писать точку с запятой в JavaScript. Пусть у нас есть такой код без точек с запятыми:

let result = 1 // в result запишется 1 let test = 2 // в test запишется 2

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

Но посмотрите на такой код:

let result = 1 + 2; // в result запишется 3

Теперь точка с запятой в конце первой строки не поставится автоматически, так как интерпретатор понимает, что команда второй строки - это часть команды первой строки.

Но если мы сами поставим точку с запятой - результат будет совсем другим:

let result = 1; // в result запишется 1 + 2; // команда ничего не делает, но и ошибки не будет

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

А теперь посмотрите на этот код:

let result = 1 +function() { return 2; }(); alert(result);

Действительно, вторая строка - просто продолжение команды первой строки и интерпретатор не ставит точку с запятой автоматически.

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

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

Точка с запятой для безопасности

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

(function() { alert(1); // выведет 1 })();

Пусть переменная num задается снаружи функции:

let num = 1; // точка с запятой стоит (function() { alert(num); // выведет 1 })();

Пусть теперь мы забыли поставить точку с запятой:

let num = 1 (function() { alert(num); //!! выдаст ошибку })();

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

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

let num = 1 ;(function() { alert(num); // выведет 1 })();

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

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

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

;(function() { alert(1); // выведет 1 })();

Определите, не запуская код, что выведется на экран:

let str = 'str'; (function() { alert(1); })();

Определите, не запуская код, что выведется на экран:

let str = 'str' (function() { alert(1); })();