Немедленно вызываемые функции (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 в виде переменной $ гарантирует, что в данном коде переменная $ не будет конфликтовать с другими библиотеками. Наконец, такая функция позволяет просто создать локальную область видимости для вашего кода и сохранить глобальную область видимости чистой.