Замыкания и функции IIFE
Замыкания
Замыкание (closure) представляют собой конструкцию, когда функция, созданная в одной области видимости, запоминает свое лексическое окружение даже в том случае, когда она выполняет вне своей области видимости. Замыкание технически включает три компонента:
- внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные — лексическое окружение
- переменные (лексическое окружение), которые определены во внешней функции
- вложенная функция, которая использует эти переменные
// внешняя функция
function outer(){
// некоторая переменная
var n;
// вложенная функция
return inner(){
// действия с переменной n
}
}
Рассмотрим замыкания на простейшем примере:
function outer(){
let x = 5;
function inner(){
x++;
console.log(x);
};
return inner;
}
// fn = inner, так как функция outer возвращает функцию inner
let fn = outer();
// 6
fn();
// 7
fn();
// 8
fn();
Здесь функция outer задает область видимости, в которой определены внутренняя функция inner и переменная x. Переменная x представляет лексическое окружение для функции inner. В самой функции inner инкрементируем переменную x и выводим ее значение на консоль. В конце функция outer возвращает функцию inner.
Далее вызываем функцию outer:
let fn = outer();
Поскольку функция outer возвращает функцию inner, то переменная fn будет хранить ссылку на функцию inner. При этом эта функция запомнила свое окружение — то есть внешнюю переменную x.
Далее мы фактически три раза вызываем функцию inner, и мы видим, что переменная x, которая определена вне функции inner, инкрементируется:
// 6
fn();
// 7
fn();
// 8
fn();
То есть несмотря на то, что переменная x определена вне функции inner, эта функция запомнила свое окружение и может его использовать, несомотря на то, что она вызывается вне функции outer, в которой была определена. В этом и суть замыканий.
Рассмотрим еще один пример:
function multiply(n){
var x = n;
return function(m){ return x * m;};
}
var fn1 = multiply(5);
var result1 = fn1(6); // 30
console.log(result1); // 30
var fn2= multiply(4);
// 24
var result2 = fn2(6);
// 24
console.log(result2);
Итак, здесь вызов функции multiply() приводит к вызову другой внутренней функции. Внутренняя же функция:
function(m){ return x * m;};
Запоминает окружение, в котором она была создана, в частности, значение переменной x.
В итоге при вызове функции multiply определяется переменная fn1, которая и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. Окружение состоит из любой локальной переменной, которая была в области действия функции multiply во время создания замыкания.
То есть fn1 — это замыкание, которое содержит и внутреннюю функцию function(m){ return x * m;}, и переменную x, которая существовала во время создания замыкания.
При создании двух замыканий: fn1 и fn2, для каждого из этих замыканий создается свое окружение.
При этом важно не запутаться в параметрах. При определении замыкания:
var fn1 = multiply(5);
Число 5 передается для параметра n функции multiply.
При вызове внутренней функции:
var result1 = fn1(6);
Число 6 передается для параметра m во внутреннюю функцию function(m){ return x * m;};.
Также мы можем использовать другой вариант для вызова замыкания:
function multiply(n){
var x = n;
return function(m){ return x * m;};
}
// 30
var result = multiply(5)(6);
console.log(result);
Самовызывающиеся функции
Обычно определение функции отделяется от ее вызова: сначала мы определяем функцию, а потом вызываем. Но это необязательно. Мы также можем создать такие функции, которые будут вызываться сразу при определении. Такие функции еще называют Immediately Invoked Function Expression (IIFE). Подобные функции заключаются в скобки, и после определения функции идет в скобках передача параметров:
(function(){
console.log("Привет мир");
}());
(function (n){
var result = 1;
for(var i=1; i<=n; i++)
result *=i;
console.log("Факториал числа " + n + " равен " + result);
}(4));