Объявление функции естественным путем

function sayHi() {
    alert("Hello, World!");
}

sayHi();  // Покажет "Hello, World!" в alert браузера.

 

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


Функциональные выражения

Тут уже все становится интереснее. Давайте посмотрим на то, как выглядят функциональные выражения в JavaScript.

var msg = "Hello, World!";
var sayHi = function() {
    alert(msg);
};

sayHi();  // Покажет "Hello, World!" в alert браузера.

На первой строке объявляется переменная msg и ей назначается строковое значение.

Строки 2-4 объявляют переменную sayHi и назначают ей значение, являющееся функцией.

На шестой строке мы вызываем эту самую функцию sayHi.


Анонимные функциональные выражения

Итак, вы уже знаете, что это такое. Пример выше и был анонимным функциональным выражением. Анонимными они являются потому, что у них не указано имя после слова function.


Именные функциональные выражения

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

var fibo = function fibonacci() {
    // тут вы можете использовать "fibonacci()"
};

// fibonacci() не сработает, а fibo() да.

Разница тут в том, что функциональное выражение имеет имя "fibonacci", которое можно использовать внутри самого выражения рекурсивным способом.


IIFE

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

!function() {
    alert("Hello from IIFE!");
}();

// Покажет alert “Hello from IIFE!”

Это, друзья мои, наша ненаглядная IIFE функция в действии. Когда вы скопируете этот код и вставите его в консоль браузера, вы увидите alert из второй строчки кода. Вот и всё, собственно. Никто никогда не увидит этот alert снова.

Это функция, которая исчезает сразу же после того, как увидит свет.

А теперь давайте разберем этот не совсем интуитивно понятный синтаксис: я знаю, вы обратили внимание на "!", если же нет, то не переживайте, вы обратили внимание на это сейчас.

Как мы видели до этого, объявление функции всегда начиналось со слова function. Всякий раз, когда JavaScript видит слово function, как вводное слово, он ожидает того, что сейчас будет объявлена функция. Чтобы этого не случилось, мы префиксим "!" перед словом function на первой строке. Это просто подталкивает JavaScript рассматривать всё, что идёт после восклицательного знака, как выражение.

Но самое интересное происходит на 3 сроке, где мы уже мгновенно вызываем это выражение.

Итак, у нас есть функциональное выражение, которое сразу же вызывается после создания. И это, друзья мои, называется IIFE, не смотря на стилистику исполнения, используемую для достижения эффекта.

Вообще, в такой вариации можно использовать не только "!", а заменить его на +, - или ~. В общем, подойдет любой унарный оператор.

Единственное, что тут делает символ !, так это превращает функцию в выражение, вместо объявления функции. И потом, сразу же, мы выполняем эту функцию.

Ещё один быстрый пример:

void function() {
    alert("Hello from IIFE!");
}();

Снова, void просто принуждает функцию к тому, чтобы ее рассматривали как выражение.

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

Но что делать, если вам нужно его получить и ещё потом где-то использовать?


Классический стиль IIFE

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

Сначала давайте посмотрим на ещё один пример функционального выражения!

(function() {
    alert("I am not an IIFE yet!");
});

В примере выше, функциональное выражение на строках 1–3 обернуто в скобки. Пока что, это не IIFE, так как это функциональное выражение никогда не запускалось и не запуститься. А теперь давайте доделаем этот код и превратим его в IIFE, для этого у нас есть два варианта стилизации:

// Variation 1
(function() {
    alert("I am an IIFE!");
}());

// Variation 2
(function() {
    alert("I am an IIFE, too!");
})();

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

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

Оба варианта широко используют.


IIFE и приватные переменные

В чем действительно хороши IIFE, так это в возможности создания области видимости.

Любые переменные внутри IIFE не видимы для внешнего мира.

Давайте посмотрим на пример.

(function IIFE_initGame() {
    // Приватные переменные к которым нет доступа за пределами IIFE
    var lives;
    var weapons;
    
    init();

    // Приватные функции к которым нет доступа за пределами IIFE
    function init() {
        lives = 5;
        weapons = 10;
    }
}());

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


IIFE с параметрами

IIFE не только могут отдавать значения, но ещё и брать аргументы во время своего вызова. Давайте посмотрим на этот короткий пример.

(function IIFE(msg, times) {
    for (var i = 1; i <= times; i++) {
        console.log(msg);
    }
}("Hello!", 5));

В примере выше, на 1й строке, IIFE чисто формально имеет два параметра с именами msg и times.

Когда мы выполняем её на 5й строке, то вместо пустых скобок, мы в них передаём аргументы IIFE.

Строки 2 и 3 используют эти параметры уже внутри IIFE.

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

(function($, global, document) {
    // используем баксик для jQuery, а global для window
}(jQuery, window, document));

В пример выше, мы передаём jQuery, window и document, как аргументы к IIFE на строке 3. Код внутри IIFE может ссылаться к ним, как к $, global, document.

Вот несколько преимуществ такой передачи параметров к IIFE.

JavaScript всегда делает поиск по области видимости заданной функции и продолжает поиск в области видимости выше, пока не найдёт указанный идентификатор. Когда мы передаём document на 3 строке, то это один и единственный раз, когда мы делаем поиск вне локальной области видимости. Любые отсылки к document в IIFE, никогда не должны искаться за пределами локальной области видимости самой IIFE. Так же и с jQuery. Даже в зависимости от сложности кода, рост производительности может быть не слишком высок, но всё равно эту фишку полезно знать.

Также, минифаеры JS могут безопасно минифицировать имена параметров, указанных в функции. Если мы не передавали эти параметры, то минифаеры не будут отсылаться к document или jQuery, так как они находятся вне области видимости этой функции.