Articles

JavaScript Closure Tutorial – Com JS Closure Example Code

Posted on

Closures – muitos de vocês já devem ter ouvido este termo JavaScript antes. Quando comecei a minha viagem com JavaScript, encontrei frequentemente encerramentos. E penso que são um dos conceitos mais importantes e interessantes em JavaScript.

Você não acha que são interessantes? Isto acontece frequentemente quando não se entende um conceito – não o achamos interessante. (Não sei se isto lhe acontece ou não, mas este é o meu caso).

Então, neste artigo, tentarei tornar os encerramentos interessantes para si.

Antes de entrar no mundo dos encerramentos, vamos primeiro compreender a delimitação lexical do âmbito. Se já o sabe, salte a próxima parte. Caso contrário, salte para dentro dela para compreender melhor os fechos.

Escopo léxico

P>Pode estar a pensar – eu conheço o âmbito local e global, mas o que raio é o âmbito léxico? Eu reagi da mesma forma quando ouvi este termo. Não se preocupe! Vamos dar uma vista de olhos mais atenta.

É simples como outros dois âmbitos:

function greetCustomer() { var customerName = "anchal"; function greetingMsg() { console.log("Hi! " + customerName); // Hi! anchal } greetingMsg();}

P>Vemos pela saída acima que a função interna pode aceder à variável da função externa. Este é o escopo léxico, onde o âmbito e o valor de uma variável é determinado pelo local onde é definida/criada (ou seja, a sua posição no código). Percebeu?

Eu sei que o último bit pode tê-lo confundido. Portanto, deixem-me levar-vos mais fundo. Sabia que a delimitação léxica do âmbito é também conhecida como delimitação estática do âmbito? Sim, esse é o seu outro nome.

Tambem há a delimitação dinâmica de âmbito, que algumas linguagens de programação suportam. Porque é que mencionei a delimitação dinâmica do âmbito de aplicação? Porque pode ajudá-lo a compreender melhor a delimitação do âmbito léxico.

Vejamos alguns exemplos:

function greetingMsg() { console.log(customerName);// ReferenceError: customerName is not defined}function greetCustomer() { var customerName = "anchal"; greetingMsg();}greetCustomer();

concorda com o resultado? Sim, dará um erro de referência. Isto porque ambas as funções não têm acesso ao alcance uma da outra, uma vez que são definidas separadamente.

Vejamos outro exemplo:

function addNumbers(number1) { console.log(number1 + number2);}function addNumbersGenerate() { var number2 = 10; addNumbers(number2);}addNumbersGenerate();

A saída acima será 20 para uma linguagem dinamicamente scoped. As línguas que suportam a delimitação lexical do âmbito darão referenceError: number2 is not defined. Why?

Because in dynamic scoping, searching takes place in the local function first, then it goes into the function that called that local function. Depois procura na função que chamou essa função, e assim por diante, na pilha de chamadas.

O nome da função é auto-explicativo – “dinâmico” significa mudança. O âmbito e valor da variável pode ser diferente, pois depende de onde a função é chamada. O significado de uma variável pode mudar em tempo de execução.

Tem a essência do escopo dinâmico? Se sim, então basta lembrar que a delimitação do âmbito léxico é o seu oposto.

Na delimitação do âmbito léxico, a pesquisa tem lugar primeiro na função local, depois vai para a função dentro da qual essa função é definida. Depois procura na função dentro da qual essa função está definida e assim por diante.

Então, a delimitação do âmbito léxico ou estática significa que o âmbito e o valor de uma variável é determinado a partir do local onde é definida. Não muda.

Vejamos novamente o exemplo acima e tentemos descobrir a saída por si próprios. Apenas uma volta – declare number2 no topo:

var number2 = 2;function addNumbers(number1) { console.log(number1 + number2);}function addNumbersGenerate() { var number2 = 10; addNumbers(number2);}addNumbersGenerate();

Sabe qual será a saída?

Correcto – são 12 para línguas lexicalmente escopadas. Isto porque primeiro procura uma função addNumbers (âmbito mais interno), depois procura para dentro, onde esta função é definida. Ao obter a variável number2, significando que a saída é 12.

pode estar a perguntar-se porque gastei tanto tempo em escopagem lexical aqui. Este é um artigo de encerramento, não um sobre a delimitação lexical do âmbito. Mas se não sabe sobre a delimitação lexical do âmbito, então não compreenderá os encerramentos.

Porquê? Terá a sua resposta quando olharmos para a definição de um encerramento. Então vamos entrar na pista e voltar aos encerramentos.

O que é um Encerramento?

Vejamos a definição de um encerramento:

O encerramento é criado quando uma função interna tem acesso às suas variáveis e argumentos de função externa. A função interna tem acesso a –
1. As suas próprias variáveis.
2. variáveis e argumentos da função externa.
3. variáveis globais.

Espere! É esta a definição de um fecho ou de uma delimitação lexical do âmbito? Ambas as definições têm o mesmo aspecto. Como são diferentes?

Bem, é por isso que defini a delimitação lexical de âmbito acima. Porque os fechos estão relacionados com a delimitação do âmbito lexical/estática.

Vejamos novamente a sua outra definição que lhe dirá como os fechos são diferentes.

O encerramento é quando uma função é capaz de aceder ao seu âmbito léxico, mesmo quando essa função está a ser executada fora do seu âmbito léxico.

Or,

As funções internas podem aceder ao seu âmbito léxico léxico, mesmo depois de a função principal já ter sido executada.

Confused? Não se preocupe se ainda não percebeu o ponto. Tenho exemplos para o ajudar a compreender melhor. Vamos modificar o primeiro exemplo de scoping lexical:

function greetCustomer() { const customerName = "anchal"; function greetingMsg() { console.log("Hi! " + customerName); } return greetingMsg;}const callGreetCustomer = greetCustomer();callGreetCustomer(); // output – Hi! anchal

A diferença neste código é que estamos a devolver a função interna e a executá-la mais tarde. Em algumas linguagens de programação, a variável local existe durante a execução da função. Mas uma vez executada a função, essas variáveis locais não existem e não serão acessíveis.

Aqui, no entanto, a cena é diferente. Após a execução da função pai, a função interna (função devolvida) ainda pode aceder às variáveis da função pai. Sim, adivinhou bem. Os encerramentos são a razão.

A função interna preserva o seu alcance léxico quando a função pai é executada e, portanto, mais tarde, essa função interna pode aceder a essas variáveis.

Para melhor o sentir, vamos usar o método dir() da consola para analisar a lista das propriedades de callGreetCustomer:

console.dir(callGreetCustomer);

Da imagem acima, é possível ver como a função interna preserva o seu escopo principal (customerName) quando greetCustomer() é executada. E mais tarde, utilizou customerName quando callGreetCustomer() foi executado.

Espero que este exemplo o tenha ajudado a compreender melhor a definição acima de um encerramento. E talvez agora ache os encerramentos um pouco mais divertidos.

Então o que se segue? Vamos tornar este tópico mais interessante olhando para diferentes exemplos.

Exemplos de encerramentos em acção

function counter() { let count = 0; return function() { return count++; };}const countValue = counter();countValue(); // 0countValue(); // 1countValue(); // 2

Cada vez que chamar countValue, o valor da variável de contagem é incrementado em 1. Espere – pensou que o valor da contagem é 0?

Bem, isso seria errado uma vez que um fecho não funciona com um valor. Armazena a referência da variável. É por isso que, quando actualizamos o valor, ele reflecte na segunda ou terceira chamada e assim por diante, uma vez que o fecho armazena a referência.

Sentindo um pouco mais claro agora? Vejamos outro exemplo:

function counter() { let count = 0; return function () { return count++; };}const countValue1 = counter();const countValue2 = counter();countValue1(); // 0countValue1(); // 1countValue2(); // 0countValue2(); // 1

br>> espero que tenha adivinhado a resposta certa. Se não, aqui está a razão. Como countValue1 e countValue2, ambos preservam o seu próprio alcance léxico. Ambos têm ambientes léxicos independentes. Pode usar dir() para verificar o valor ] em ambos os casos.

Vejamos um terceiro exemplo.

Este é um pouco diferente. Nele, temos de escrever uma função para alcançar a saída:

const addNumberCall = addNumber(7);addNumberCall(8) // 15addNumberCall(6) // 13

Simples. Use o seu novo conhecimento de encerramento:

function addNumber(number1) { return function (number2) { return number1 + number2; };}

Agora vamos ver alguns exemplos complicados:

function countTheNumber() { var arrToStore = ; for (var x = 0; x < 9; x++) { arrToStore = function () { return x; }; } return arrToStore;}const callInnerFunctions = countTheNumber();callInnerFunctions() // 9callInnerFunctions() // 9

Cada elemento de matriz que armazena uma função dar-lhe-á uma saída de 9. Adivinhou bem? Espero que sim, mas ainda assim deixe-me dizer-lhe a razão. Isto é devido ao comportamento do fecho.

O fecho armazena a referência, não o valor. A primeira vez que o laço corre, o valor de x é 0. Depois a segunda vez x é 1, e assim por diante. Porque o fecho armazena a referência, cada vez que o laço corre está a mudar o valor de x. E finalmente, o valor de x será 9. Então callInnerFunctions() dá uma saída de 9.

Mas e se quiser uma saída de 0 a 8? Simples! Use um fecho.

P>Pense nisso antes de olhar para a solução abaixo:

function callTheNumber() { function getAllNumbers(number) { return function() { return number; }; } var arrToStore = ; for (var x = 0; x < 9; x++) { arrToStore = getAllNumbers(x); } return arrToStore;}const callInnerFunctions = callTheNumber();console.log(callInnerFunctions()); // 0console.log(callInnerFunctions()); // 1

Aqui, criámos um âmbito separado para cada iteração. Pode usar console.dir(arrToStore) para verificar o valor de x em ] para diferentes elementos da matriz.

É isso! Espero que agora possa dizer que acha os fechos interessantes.

Para ler os meus outros artigos, verifique o meu perfil aqui.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *