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:

  1. Uma função externa que define um escopo e contém algumas variáveis - o ambiente léxico.

  2. Variáveis (o ambiente léxico) que são definidas na função externa.

  3. 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 define um escopo onde a função interna inner e a variável x são definidas. x representa o ambiente léxico para inner. inner incrementa x e o imprime. A função outer retorna inner.

const fn = outer();

Como outer retorna inner, fn armazena uma referência a inner, relembrando seu ambiente - a variável x.

Chamando inner três vezes, vemos x sendo incrementada:

fn(); // 6
fn(); // 7
fn(); // 8

Apesar de x ser definida fora de inner, inner se lembra de seu ambiente e pode usá-lo, mesmo sendo chamada fora de 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 e fn2 têm suas próprias cópias de x, que manipulam independentemente.

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() cria uma função interna que se lembra de 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, uma constante fn1 é definida, representando um closure que combina duas coisas: a função e o ambiente onde foi criada. O ambiente consiste em quaisquer variáveis locais disponíveis no escopo da função multiply durante a criação do closure.

Ou seja, fn1 é um closure que contém a função interna function(m) { return x * m; } e a variável x existente na criação do closure.

Na criação de dois closures, fn1 e fn2, cada um tem seu próprio ambiente.

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 da função multiply.

Ao chamar a função interna:

const result1 = fn1(6);

O número 6 é passado para o parâmetro m na função interna 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 recebe dois parâmetros e define três funções aninhadas. Essas funções constituem closures, utilizando os parâmetros da função person como seu ambiente léxico.

Aqui, person atua como um construtor para um objeto de uma pessoa, com name e age representando seu nome e idade, respectivamente. As três funções aninhadas - print, work e incrementAge - acessam seu ambiente léxico, que consiste nos parâmetros de person.

Para tornar essas funções acessíveis externamente, person as retorna como uma matriz:

return [print, work, incrementAge];

Chamando person com parâmetros, obtemos seu resultado :

const tom = person("Tom", 39);

O que é tom? Essencialmente, é uma matriz de três funções que permitem manipular a pessoa tom. Podemos chamar uma dessas funções diretamente:

tom[0](); // print

Ou seja, tom[0] retorna print, e tom[0]() a chama. O console do navegador exibe:

Person Tom (39)
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com