Немедленно вызываемые функции (Immediately-Invoked Function Expression) - IIFE
JavaScript

Немедленно вызываемые функции (Immediately-Invoked Function Expression) - IIFE

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

Немедленно вызываемая функция выглядит так:


(function () {
    // ...
}());

Или так:


(function () {
    // Code goes here
})();

Оба эти варианта эквивалентны. Определяя функцию, например, function doSomething() {} или var doSomething = function() {}, мы тем самым создаем идентификатор функции, которую можно вызвать, добавив круглые скобки: doSomething(). Таким образом, чтобы немедленно вызвать функцию, необходимо после функционального выражения поставить круглые скобки. Однако просто добавление круглых скобок вызовет синтаксическую ошибку, само функциональное выражение, в свою очередь, тоже нужно обернуть в скобки.

О скобках

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


function(){ /* code */ }(); /* SyntaxError: function statement requires a name */

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

Еще один «плохой» пример:


function foo(){ /* code */ }(); /* SyntaxError: syntax error */

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

Еще один пример:


function foo(){ /* code */ }( 1 + 1 );

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

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


var value = function() { /* ... */ }();

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

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

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


(function sum(a, b) {
    console.log(a + b); // 5
})(2, 3);

Возвращаемое значение немедленно вызываемой функции

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


var result = (function(a, b) { 
    return a + b;
})(2, 5);

Прием немедленно вызываемой функции испольузется для “фиксации” значения, изменяемого в цикле.

Рассмотрим классический пример:


var elements = document.getElementsByTagName('a');
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', function(e) {
    e.preventDefault();
    alert('Link #' + i);
  }, false);
}

Мы выбираем массив ссылок и на каждую из них навешиваем обработчик события click. Ожидается, что по клику на ссылку будет выведен ее порядковый номер в массиве. Но данный пример не будет работать корректно. Пока наступит событие click, значение переменной i изменится, и клик по всем ссылкам будет выводить только последнее значение индекса i.

Для исправления этой ситуации используется немедленно вызываемая функция. Мы передаем текущее значение i в эту функцию и, тем самым, «фиксируем» его:


var elements = document.getElementsByTagName('a');
for (var i = 0; i < elements.length; i++) {
  (function(localIndex){
    elements[i].addEventListener('click', function(e) {
      e.preventDefault();
      alert('Link #' + localIndex);
    }, false);
  })(i);
}

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


var elements = document.getElementsByTagName('a');
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', (function(localIndex){
    return function(e) {
      e.preventDefault();
      alert('Link #' + localIndex);
    };
  })(i), false);
}

Шаблон модуль

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


var counter = (function() {
  var i = 0;

  return {
    get: function(){
      return i;
    },
    set: function( val ){
      i = val;
    },
    increment: function() {
      return ++i;
    }
  };
})();

console.log(counter.get()); // 0
counter.set(3);
console.log(counter.increment()); // 4
console.log(counter.increment()); // 5

console.log(counter.i); // undefined
i; // ReferenceError: i is not defined

Заключение

Шаблон немедленно вызываемой функции, с одной стороны, достаточно простой, с другой — крайне полезный. С помощью немедленно вызываемой функции реализуется прием «фиксации» изменяемого в цикле значения. На немедленно вызываемой функции основан шаблон модуль.

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

Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться