Closures - JavaScript
Um closure (fechamento) é uma estrutura na qual uma função criada em um escopo retém seu ambiente léxico, mesmo quando executada fora desse escopo.
Três componentes são necessários para criar um closure:
Uma função externa que define um escopo e contém algumas variáveis - o ambiente léxico.
Variáveis (o ambiente léxico) que são definidas na função externa.
Uma função interna que utiliza essas variáveis.
function outer() { // Função externa
let n; // Uma variável
return function inner() { // Função interna
// Ações com a variável n
}
}
Basicamente, a função inner é um closure, relembrando seu ambiente léxico, que inclui a variável n.
Vamos examinar fechamentos com um exemplo simples:
function outer() {
let x = 5;
function inner() {
x++;
console.log(x);
}
return inner;
}
const fn = outer(); // `fn` se refere à função interna `inner`, pois `outer` a retorna
fn(); // 6
fn(); // 7
fn(); // 8
Aqui, a função outer
inner
x
inner
inner
x
outer
inner
const fn = outer();
Como outer retorna inner
fn
inner
x
Chamando inner
x
fn(); // 6 fn(); // 7 fn(); // 8
Apesar de x
inner
inner
outer
Cada closure tem seu próprio ambiente:
function outer() {
let x = 5;
function inner() {
x++;
console.log(x);
}
return inner;
}
const fn1 = outer();
const fn2 = outer();
fn1(); // 6
fn1(); // 7
fn2(); // 6
fn2(); // 7
fn1
fn2
x
Outro exemplo:
function multiply(n) {
let x = n;
return function(m) {
return x * m;
};
}
const fn1 = multiply(5);
const result1 = fn1(6); // 30
console.log(result1); // 30
const fn2 = multiply(4);
const result2 = fn2(6); // 24
console.log(result2); // 24
Aqui, multiply()
x
function(m) { return x * m; }
Essa função lembra o ambiente no qual foi criada, em particular, o valor da variável x
Portanto, ao chamar a função multiply
fn1
multiply
Ou seja, fn1
function(m) { return x * m; }
x
Na criação de dois closures, fn1
fn2
Vale a pena ter cuidado com os parâmetros. Ao definir o closure:
const fn1 = multiply(5);
O número 5 é passado para o parâmetro n
multiply
Ao chamar a função interna:
const result1 = fn1(6);
O número 6 é passado para o parâmetro m
function(m) { return x * m; }
Também podemos usar outra maneira de invocar o closure:
const result = multiply(5)(6); // 30
console.log(result);
Closures e Programação Orientada a Objetos
Embora a programação orientada a objetos seja tratada posteriormente, é notável que os closures sejam precursores dessa abordagem. De certo modo, o uso de closures permite imitar a criação e manipulação de objetos. Considere o código a seguir:
function person(name, age) {
console.log("Person", name, "created");
function print() {
console.log("Person ", name, " (" + age + ")");
}
function work() {
console.log("Person ", name, " works");
}
function incrementAge(value) {
age += value;
}
return [print, work, incrementAge];
}
const tom = person("Tom", 39);
tom[0](); // print
tom[1](); // work
tom[2](1) ; // incrementAge
tom[0](); // print
O console do navegador exibe:
Person Tom created Person Tom (39) Person Tom works Person Tom (40)
A função person
person
Aqui, person
name
age
print
work
incrementAge
person
Para tornar essas funções acessíveis externamente, person
return [print, work, incrementAge];
Chamando person com parâmetros, obtemos seu resultado :
const tom = person("Tom", 39);
O que é tom
tom
tom[0](); // print
Ou seja, tom[0]
tom[0]()
Person Tom (39)