Como você explicaria os fechamentos de JavaScript para alguém com conhecimento dos conceitos em que eles consistem (por exemplo, funções, variáveis e similares), mas que não entendem eles mesmos?
Vi o exemplo de esquema dado na Wikipedia, mas infelizmente não ajudou.
closure
código difícil de ler , em vez doObject Literal
que se reutiliza e reduz a sobrecarga da mesma forma, mas exige 100% menos código de quebra.Respostas:
Um encerramento é um par de:
Um ambiente lexical é parte de todo contexto de execução (quadro de pilha) e é um mapa entre identificadores (ou seja, nomes de variáveis locais) e valores.
Toda função no JavaScript mantém uma referência ao seu ambiente lexical externo. Esta referência é usada para configurar o contexto de execução criado quando uma função é chamada. Essa referência permite que o código dentro da função "veja" variáveis declaradas fora da função, independentemente de quando e onde a função é chamada.
Se uma função foi chamada por uma função, que por sua vez foi chamada por outra função, uma cadeia de referências a ambientes lexicais externos é criada. Essa cadeia é chamada de cadeia de escopo.
No código a seguir,
inner
forma um fechamento com o ambiente lexical do contexto de execução criado quandofoo
é chamado, fechando sobre a variávelsecret
:Em outras palavras: em JavaScript, as funções carregam uma referência a uma "caixa de estado" privada, à qual somente elas (e quaisquer outras funções declaradas no mesmo ambiente lexical) têm acesso. Essa caixa de estado é invisível para o chamador da função, fornecendo um excelente mecanismo para ocultar e encapsular dados.
E lembre-se: as funções no JavaScript podem ser passadas como variáveis (funções de primeira classe), o que significa que esses pares de funcionalidade e estado podem ser passados pelo seu programa: semelhante à maneira como você pode passar uma instância de uma classe no C ++.
Se o JavaScript não tivesse encerramentos, mais estados teriam que ser passados entre as funções explicitamente , tornando as listas de parâmetros mais longas e com o código mais barulhento.
Portanto, se você deseja que uma função sempre tenha acesso a um pedaço de estado particular, você pode usar um encerramento.
... e muitas vezes nós não queremos estado associado com uma função. Por exemplo, em Java ou C ++, quando você adiciona uma variável de instância privada e um método a uma classe, você está associando estado à funcionalidade.
Em C e na maioria dos outros idiomas comuns, após o retorno de uma função, todas as variáveis locais não são mais acessíveis porque o quadro da pilha é destruído. No JavaScript, se você declarar uma função dentro de outra função, as variáveis locais da função externa poderão permanecer acessíveis após o retorno dela. Dessa forma, no código acima,
secret
permanece disponível para o objeto de funçãoinner
, depois que ele é retornadofoo
.Usos dos fechamentos
Os fechamentos são úteis sempre que você precisar de um estado privado associado a uma função. Esse é um cenário muito comum - e lembre-se: o JavaScript não tinha uma sintaxe de classe até 2015 e ainda não possui uma sintaxe de campo privado. Os fechamentos atendem a essa necessidade.
Variáveis de instância privada
No código a seguir, a função
toString
fecha sobre os detalhes do carro.Programação Funcional
No código a seguir, a função
inner
fecha sobre ambosfn
eargs
.Programação Orientada a Eventos
No código a seguir, a função
onClick
fecha sobre a variávelBACKGROUND_COLOR
.Modularização
No exemplo a seguir, todos os detalhes da implementação estão ocultos dentro de uma expressão de função executada imediatamente. As funções
tick
etoString
fecham sobre o estado privado e as funções necessárias para concluir seu trabalho. Os fechamentos nos permitiram modularizar e encapsular nosso código.Exemplos
Exemplo 1
Este exemplo mostra que as variáveis locais não são copiadas no fechamento: o fechamento mantém uma referência às próprias variáveis originais . É como se o quadro da pilha permanecesse vivo na memória, mesmo após a saída da função externa.
Exemplo 2
No código a seguir, três métodos
log
,increment
eupdate
toda apertada sobre o mesmo ambiente lexical.E toda vez que
createObject
é chamado, um novo contexto de execução (quadro da pilha) é criado e uma variável completamente novax
, e um novo conjunto de funções (log
etc.) são criados, que fecham sobre essa nova variável.Exemplo 3
Se você estiver usando variáveis declaradas
var
, tenha cuidado para entender qual variável você está fechando. Variáveis declaradas usandovar
são içadas. Isso é muito menos problemático no JavaScript moderno devido à introdução delet
econst
.No código a seguir, toda vez que o loop
inner
é criado, uma nova função é encerradai
. Mas, comovar i
é içada fora do loop, todas essas funções internas se fecham sobre a mesma variável, o que significa que o valor final dei
(3) é impresso três vezes.Pontos finais:
function
de dentro de outra função é o exemplo clássico de um fechamento, porque o estado dentro da função externa está implicitamente disponível para a função interna retornada, mesmo após a função externa ter concluído a execução.eval()
dentro de uma função, um fechamento é usado. O texto vocêeval
pode referenciar variáveis locais da função e, no modo não estrito, você pode até criar novas variáveis locais usandoeval('var foo = …')
.new Function(…)
(o construtor Function ) dentro de uma função, ela não fecha seu ambiente lexical: fecha-se sobre o contexto global. A nova função não pode fazer referência às variáveis locais da função externa.Ligações
fonte
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
do developer.mozilla.org/en-US/docs/Web/JavaScript/Closureslet i = 0
vez devar i = 0
no Exemplo 5, eletestList()
imprimirá o que deseja originalmente.Toda função em JavaScript mantém um link para seu ambiente lexical externo. Um ambiente lexical é um mapa de todos os nomes (por exemplo, variáveis, parâmetros) dentro de um escopo, com seus valores.
Portanto, sempre que você vir a
function
palavra - chave, o código dentro dessa função terá acesso a variáveis declaradas fora da função.Isso registrará
16
porque a funçãobar
fecha sobre o parâmetrox
e a variáveltmp
, ambas existentes no ambiente lexical da função externafoo
.Função
bar
, juntamente com seu vínculo com o ambiente lexical da função,foo
é um fechamento.Uma função não precisa retornar para criar um fechamento. Simplesmente em virtude de sua declaração, toda função se fecha sobre seu ambiente lexical, formando um fechamento.
A função acima também registrará 16, porque o código interno
bar
ainda pode se referir ao argumentox
e à variáveltmp
, mesmo que eles não estejam mais diretamente no escopo.No entanto, como
tmp
ainda está pendurado no interiorbar
do fechamento, está disponível para ser incrementado. Ele será incrementado toda vez que você ligarbar
.O exemplo mais simples de fechamento é o seguinte:
Quando uma função JavaScript é chamada, um novo contexto de execução
ec
é criado. Juntamente com os argumentos da função e o objeto de destino, esse contexto de execução também recebe um link para o ambiente lexical do contexto de execução de chamada, o que significa que as variáveis declaradas no ambiente lexical externo (no exemplo acima, ambosa
eb
) estão disponíveis emec
.Toda função cria um fechamento porque toda função tem um link para seu ambiente lexical externo.
Observe que as próprias variáveis são visíveis de dentro de um fechamento, não de cópias.
fonte
delete
falha. No entanto, o ambiente lexical que a função carregará como [[Scope]] (e, finalmente, usar como base para seu próprio ambiente lexical quando invocado) é determinado quando a instrução que define a função é executada. Isso significa que a função está fechando o conteúdo INTEIRO do escopo em execução, independentemente de quais valores ela realmente se refere e se ela escapa ao escopo. Por favor, olhe seções 13.2 e 10 em specPREFÁCIO: esta resposta foi escrita quando a pergunta era:
Tenho certeza de que fui uma das únicas pessoas que tentaram levar a pergunta inicial literalmente. Desde então, a pergunta mudou várias vezes, então minha resposta agora pode parecer incrivelmente boba e fora de lugar. Espero que a ideia geral da história continue sendo divertida para alguns.
Sou um grande fã de analogia e metáfora ao explicar conceitos difíceis, então deixe-me tentar minha mão com uma história.
Era uma vez:
Havia uma princesa ...
Ela viveu em um mundo maravilhoso, cheio de aventuras. Ela conheceu seu príncipe encantado, viajou pelo mundo em um unicórnio, lutou contra dragões, encontrou animais falantes e muitas outras coisas fantásticas.
Mas ela sempre teria que voltar ao seu mundo monótono de tarefas e adultos.
E ela costumava contar a eles sobre sua última e incrível aventura como princesa.
Mas tudo o que veriam é uma garotinha ...
... contando histórias sobre magia e fantasia.
E mesmo que os adultos soubessem de princesas reais, eles nunca acreditariam nos unicórnios ou dragões porque nunca poderiam vê-los. Os adultos disseram que só existiam dentro da imaginação da menininha.
Mas nós sabemos a verdade real; que a menina com a princesa por dentro ...
... é realmente uma princesa com uma garotinha por dentro.
fonte
story()
função, que é a única interface que alittleGirl
instância expõe no mundo da magia.story
está o fechamento, mas, se o código tivesse sidovar story = function() {}; return story;
,littleGirl
seria o fechamento. Pelo menos, é a impressão que recebo do uso de métodos 'privados' pelo MDN com fechamentos : "Essas três funções públicas são encerramentos que compartilham o mesmo ambiente".story
é um fechamento que faz referência ao ambiente fornecido no escopo deprincess
.princess
também é outro fechamento implícito , ou seja, oprincess
e olittleGirl
compartilhariam qualquer referência a umaparents
matriz que existiria no ambiente / escopo em quelittleGirl
existe eprincess
está definido.princess
que o que está escrito. Infelizmente, esta história está um pouco fora do lugar neste tópico. Originalmente, a pergunta era "explicar os fechamentos de JavaScript para os 5 anos"; minha resposta foi a única que tentou fazer isso. Não duvido que teria falhado miseravelmente, mas pelo menos essa resposta poderia ter tido a chance de manter o interesse de uma criança de 5 anos.Levando a questão a sério, devemos descobrir o que uma criança de 6 anos de idade é capaz de cognitivamente, embora seja certo que alguém interessado em JavaScript não seja tão típico.
Sobre o desenvolvimento infantil: 5 a 7 anos , diz:
Podemos usar este exemplo para explicar os fechamentos, da seguinte maneira:
Podemos codificar isso em JavaScript assim:
Outros pontos que explicam por que os fechamentos são interessantes:
makeKitchen()
é chamado, um novo fechamento é criado com seu própriotrashBags
.trashBags
variável é local para o interior de cada cozinha e não é acessível fora, mas a função interna nogetTrashBag
propriedade tem acesso a ela.getTrashBag
função faz isso aqui.fonte
The Straw Man
Preciso saber quantas vezes um botão foi clicado e fazer algo a cada terceiro clique ...
Solução bastante óbvia
Agora isso vai funcionar, mas invade o escopo externo ao adicionar uma variável, cujo único objetivo é acompanhar a contagem. Em algumas situações, isso seria preferível, pois seu aplicativo externo pode precisar acessar essas informações. Mas, nesse caso, estamos apenas alterando o comportamento de cada terceiro clique, portanto, é preferível incluir essa funcionalidade dentro do manipulador de eventos .
Considere esta opção
Observe algumas coisas aqui.
No exemplo acima, estou usando o comportamento de fechamento do JavaScript. Esse comportamento permite que qualquer função tenha acesso ao escopo em que foi criada, indefinidamente. Para praticamente aplicar isso, invoco imediatamente uma função que retorna outra função e, como a função que estou retornando, tem acesso à variável de contagem interna (devido ao comportamento de fechamento explicado acima), isso resulta em um escopo privado para uso pelo resultado função ... Não é tão simples? Vamos diluir ...
Um fechamento simples de uma linha
Todas as variáveis fora da função retornada estão disponíveis para a função retornada, mas não estão diretamente disponíveis para o objeto da função retornada ...
Pegue? Portanto, em nosso exemplo principal, a variável count está contida no fechamento e sempre disponível para o manipulador de eventos, portanto, mantém seu estado de clique para clicar.
Além disso, esse estado de variável privada é totalmente acessível, para leituras e atribuições às suas variáveis de escopo privadas.
Ai está; Agora você está encapsulando totalmente esse comportamento.
Postagem completa no blog (incluindo considerações sobre jQuery)
fonte
É difícil explicar os fechamentos porque são usados para fazer funcionar algum comportamento que todo mundo espera intuitivamente que funcione de qualquer maneira. Acho que a melhor maneira de explicá-las (e da maneira que eu aprendi o que eles fazem) é de imaginar a situação sem eles:
O que aconteceria aqui se o JavaScript não conhecesse fechamentos? Apenas substitua a chamada na última linha pelo corpo do método (que é basicamente o que as chamadas de função fazem) e você obtém:
Agora, onde está a definição de
x
? Não o definimos no escopo atual. A única solução é deixarplus5
levar seu escopo (ou melhor, o escopo de seus pais). Dessa forma,x
é bem definido e está vinculado ao valor 5.fonte
TLDR
Um fechamento é um link entre uma função e seu ambiente lexical externo (isto é, conforme escrito), de modo que os identificadores (variáveis, parâmetros, declarações de função etc.) definidos nesse ambiente sejam visíveis de dentro da função, independentemente de quando ou de onde a função é chamada.
Detalhes
Na terminologia da especificação ECMAScript, pode-se dizer que um fechamento é implementado pela
[[Environment]]
referência de cada objeto de função, que aponta para o ambiente lexical no qual a função é definida.Quando uma função é chamada pelo
[[Call]]
método interno , a[[Environment]]
referência no objeto de função é copiada na referência do ambiente externo do registro de ambiente do contexto de execução recém-criado (quadro de pilha).No exemplo a seguir, a função
f
fecha-se sobre o ambiente lexical do contexto de execução global:No exemplo a seguir, a função
h
fecha-se sobre o ambiente lexical da funçãog
, que, por sua vez, fecha-se sobre o ambiente lexical do contexto de execução global.Se uma função interna for retornada por uma externa, o ambiente lexical externo persistirá após o retorno da função externa. Isso ocorre porque o ambiente lexical externo precisa estar disponível se a função interna for eventualmente invocada.
No exemplo a seguir, a função se
j
fecha sobre o ambiente lexical da funçãoi
, o que significa que a variávelx
é visível a partir da função internaj
, muito depois que a funçãoi
concluir a execução:Em um fecho, as variáveis do ambiente exterior lexical -se são disponíveis, não cópias.
A cadeia de ambientes lexicais, vinculada entre contextos de execução por meio de referências ao ambiente externo, forma uma cadeia de escopo e define os identificadores visíveis de qualquer função.
Observe que, na tentativa de melhorar a clareza e a precisão, essa resposta foi substancialmente alterada da original.
fonte
console.log
assim. Se alguém estiver interessado, há mais: developer.mozilla.org/pt-BR/docs/DOM/…var
).OK, fã de fechamentos de 6 anos de idade. Deseja ouvir o exemplo mais simples de fechamento?
Vamos imaginar a próxima situação: um motorista está sentado em um carro. Aquele carro está dentro de um avião. Avião está no aeroporto. A capacidade do motorista de acessar coisas fora de seu carro, mas dentro do avião, mesmo que esse avião saia de um aeroporto, é um fechamento. É isso aí. Quando você completa 27 anos, veja a explicação mais detalhada ou o exemplo abaixo.
Aqui está como eu posso converter minha história de avião no código.
fonte
Esta é uma tentativa de esclarecer vários (possíveis) mal-entendidos sobre fechamentos que aparecem em algumas das outras respostas.
fonte
Eu escrevi um post de blog há algum tempo explicando os fechamentos. Aqui está o que eu disse sobre fechamentos em termos de por que você quer um.
Nesse sentido, eles permitem que uma função atue um pouco como um objeto com atributos privados.
Post completo:
Então, quais são essas coisas de fechamento?
fonte
devError = emailError("[email protected]", errorString)
e ter minha própria versão personalizada de uma função emailError compartilhada?Os fechamentos são simples:
O exemplo simples a seguir abrange todos os principais pontos de fechamento de JavaScript. *
Aqui está uma fábrica que produz calculadoras que podem adicionar e multiplicar:
O ponto principal: Cada chamada para
make_calculator
cria uma nova variável localn
, que continua a ser utilizável pela calculadoraadd
emultiply
funciona por muito tempo depois dosmake_calculator
retornos.Se você conhece os quadros de pilha, essas calculadoras parecem estranhas: como elas podem continuar acessando
n
apósmake_calculator
retornos? A resposta é imaginar que o JavaScript não usa "quadros de pilha", mas usa "quadros de heap", que podem persistir após a chamada de função que os fez retornar.Funções internas como
add
emultiply
, que acessam variáveis declaradas em uma função externa ** , são chamadas de fechamentos .Isso é praticamente tudo o que há para fechamentos.
* Por exemplo, abrange todos os pontos do artigo "Fechamentos para manequins", fornecidos em outra resposta , exceto o exemplo 6, que mostra simplesmente que as variáveis podem ser usadas antes de serem declaradas, um fato interessante a ser conhecido, mas completamente não relacionado aos fechamentos. Ele também abrange todos os pontos da resposta aceita , exceto os pontos (1) que funções copiam seus argumentos em variáveis locais (argumentos da função nomeada) e (2) que a cópia de números cria um novo número, mas a referência de objeto fornece outra referência ao mesmo objeto. Também é bom saber disso, mas novamente não relacionado a fechamentos. Também é muito semelhante ao exemplo nesta resposta, mas um pouco mais curto e menos abstrato. Não cobre o ponto deesta resposta ou atual este comentário, que é o JavaScript que dificulta a conexão dovalor de uma variável de loop em sua função interna: A etapa "plug-in" pode ser realizada apenas com uma função auxiliar que encerra sua função interna e é invocada em cada iteração de loop. (Estritamente falando, a função interna acessa a cópia da variável da função auxiliar, em vez de ter alguma coisa conectada.) Novamente, muito útil ao criar fechamentos, mas não faz parte do que é um fechamento ou como funciona. Existe uma confusão adicional devido aos fechamentos que funcionam de maneira diferente em linguagens funcionais como o ML, em que as variáveis são vinculadas a valores e não a espaço de armazenamento, fornecendo um fluxo constante de pessoas que entendem os fechamentos de uma maneira (ou seja, a maneira "conectada") que é simplesmente incorreto para JavaScript, onde as variáveis estão sempre ligadas ao espaço de armazenamento e nunca aos valores.
** Qualquer função externa, se várias estiverem aninhadas, ou mesmo no contexto global, como esta resposta aponta claramente.
fonte
first_calculator
é um objeto (não uma função), você não deve usar parêntesessecond_calculator = first_calculator;
, pois é uma atribuição, não uma chamada de função. Para responder sua pergunta, haveria apenas uma chamada para make_calculator, para que apenas uma calculadora fosse feita e as variáveis first_calculator e second_calculator se referissem à mesma calculadora, portanto as respostas seriam 3, 403, 4433, 44330.Como eu explicaria isso a uma criança de seis anos:
Você sabe como os adultos podem possuir uma casa, e eles chamam de lar? Quando uma mãe tem um filho, o filho realmente não possui nada, certo? Mas seus pais possuem uma casa; portanto, sempre que alguém perguntar à criança "Onde está sua casa?", Ela poderá responder "aquela casa!" E apontar para a casa de seus pais. Um "fechamento" é a capacidade da criança de sempre (mesmo no exterior) poder dizer que tem uma casa, mesmo que sejam realmente os pais que são os donos da casa.
fonte
Você pode explicar o fechamento de uma criança de 5 anos? *
Ainda acho que a explicação do Google funciona muito bem e é concisa:
* Questão AC #
fonte
Costumo aprender melhor comparando BOM / MAU. Eu gosto de ver o código de trabalho seguido pelo código não funcional que alguém provavelmente encontrará. Eu montei um jsFiddle que faz uma comparação e tenta resumir as diferenças para as explicações mais simples que eu possa ter.
Fechamentos feitos corretamente:
No código acima,
createClosure(n)
é invocado em todas as iterações do loop. Observe que eu nomeei a variáveln
para destacar que ela é uma nova variável criada em um novo escopo de função e não é a mesma variávelindex
que está vinculada ao escopo externo.Isso cria um novo escopo e
n
está vinculado a esse escopo; isso significa que temos 10 escopos separados, um para cada iteração.createClosure(n)
retorna uma função que retorna n dentro desse escopo.Dentro de cada escopo,
n
é vinculado a qualquer valor que tinha quandocreateClosure(n)
foi chamado, portanto a função aninhada que é retornada sempre retornará o valorn
que tinha quandocreateClosure(n)
foi chamado.Fechamentos feitos incorretamente:
No código acima, o loop foi movido dentro da
createClosureArray()
função e a função agora retorna o array completo, que à primeira vista parece mais intuitivo.O que pode não ser óbvio é que, uma vez que
createClosureArray()
é chamado apenas uma vez que apenas um escopo é criado para esta função, em vez de um para cada iteração do loop.Dentro desta função, uma variável denominada
index
é definida. O loop é executado e adiciona funções à matriz que retornaindex
. Observe queindex
é definido nacreateClosureArray
função que apenas é invocada uma vez.Como havia apenas um escopo dentro da
createClosureArray()
função,index
é vinculado apenas a um valor dentro desse escopo. Em outras palavras, cada vez que o loop altera o valor deindex
, ele é alterado para tudo o que faz referência a esse escopo.Todas as funções adicionadas à matriz retornam a
index
variável SAME do escopo pai onde foi definida, em vez de 10 diferentes de 10 escopos diferentes, como no primeiro exemplo. O resultado final é que todas as 10 funções retornam a mesma variável do mesmo escopo.Depois que o loop terminou e
index
foi modificado, o valor final era 10, portanto, todas as funções adicionadas à matriz retornam o valor daindex
variável única que agora está definida como 10.Resultado
fonte
let
paravar
corrige a diferença.n
criadas em um novo fechamento. Nós apenas retornamos uma função para que possamos armazená-la na matriz e invocá-la mais tarde.arr[index] = (function (n) { return 'n = ' + n; })(index);
. Mas então você está armazenando a string resultante na matriz, em vez de uma função para invocar, o que derrota o ponto do meu exemplo.Wikipedia sobre fechamentos :
Tecnicamente, em JavaScript , toda função é um encerramento . Ele sempre tem acesso a variáveis definidas no escopo circundante.
Como a construção de definição de escopo no JavaScript é uma função , não um bloco de código como em muitas outras linguagens, o que geralmente queremos dizer com fechamento no JavaScript é uma função que trabalha com variáveis não-locais definidas na função circundante já executada .
Os fechamentos são frequentemente usados para criar funções com alguns dados privados ocultos (mas nem sempre é o caso).
ems
O exemplo acima está usando uma função anônima, que foi executada uma vez. Mas não precisa ser assim. Ele pode ser nomeado (por exemplo
mkdb
) e executado posteriormente, gerando uma função de banco de dados toda vez que é chamada. Toda função gerada terá seu próprio objeto de banco de dados oculto. Outro exemplo de uso de fechamentos é quando não retornamos uma função, mas um objeto que contém várias funções para propósitos diferentes, cada uma dessas funções tendo acesso aos mesmos dados.fonte
Eu montei um tutorial interativo em JavaScript para explicar como funcionam os fechamentos. O que é um fechamento?
Aqui está um dos exemplos:
fonte
Os segredos das funções JavaScript são as variáveis privadas
Sempre que você o chama, a variável local "name" é criada e recebe o nome "Mary". E toda vez que a função sai, a variável é perdida e o nome é esquecido.
Como você pode imaginar, como as variáveis são recriadas toda vez que a função é chamada e ninguém mais as conhece, deve haver um local secreto onde elas são armazenadas. Pode ser chamado de Câmara Secreta ou pilha ou escopo local mas isso realmente não importa. Sabemos que eles estão lá, em algum lugar, escondidos na memória.
Mas, no JavaScript, existe uma coisa muito especial: as funções criadas dentro de outras funções também podem conhecer as variáveis locais de seus pais e mantê-las enquanto viverem.
Portanto, enquanto estivermos na função pai, ele poderá criar uma ou mais funções filho que compartilham as variáveis secretas do local secreto.
Mas o triste é que, se a criança também for uma variável privada de sua função pai, ela também morrerá quando o pai terminar, e os segredos morrerão com ela.
Então, para viver, a criança tem que sair antes que seja tarde demais
E agora, mesmo que Maria "não esteja mais fugindo", a memória dela não se perde e seu filho sempre se lembrará de seu nome e de outros segredos que eles compartilharam durante o tempo que passaram juntos.
Então, se você chamar a criança de "Alice", ela responderá
É tudo o que há para dizer.
fonte
Não entendo por que as respostas são tão complexas aqui.
Aqui está um encerramento:
Sim. Você provavelmente usa isso muitas vezes ao dia.
fonte
a
. Na minha opinião, a surpresa é que o objeto de escopo JS efetivamente fornecea
como uma propriedade e não como uma constante. E você só notará esse comportamento importante se modificá-lo, como emreturn a++;
Exemplo para o primeiro ponto por dlaliberte:
fonte
Um fechamento é o local onde uma função interna tem acesso a variáveis em sua função externa. Essa é provavelmente a explicação mais simples de uma linha que você pode obter sobre os fechamentos.
fonte
Sei que já existem muitas soluções, mas acho que esse script pequeno e simples pode ser útil para demonstrar o conceito:
fonte
Você está dormindo e convida Dan. Você diz a Dan para trazer um controlador XBox.
Dan convida Paul. Dan pede a Paul para trazer um controlador. Quantos controladores foram trazidos para a festa?
fonte
O autor de Closures explicou muito bem os fechamentos, explicando o motivo pelo qual precisamos deles e também o LexicalEnvironment necessário para entender os fechamentos.
Aqui está o resumo:
E se uma variável for acessada, mas não for local? Como aqui:
Nesse caso, o intérprete encontra a variável no exterior
LexicalEnvironment
objeto .O processo consiste em duas etapas:
Quando uma função é criada, ela obtém uma propriedade oculta, denominada [[Scope]], que faz referência ao LexicalEnvironment atual.
Se uma variável for lida, mas não puder ser encontrada em nenhum lugar, será gerado um erro.
Funções aninhadas
As funções podem ser aninhadas uma dentro da outra, formando uma cadeia de LexicalEnvironments, que também pode ser chamada de cadeia de escopo.
Portanto, a função g tem acesso a g, a e f.
Encerramentos
Uma função aninhada pode continuar ativa após a conclusão da função externa:
Como marcar LexicalEnvironments:
Como vemos,
this.say
é uma propriedade no objeto de usuário, portanto continua ativa após a conclusão do Usuário.E se você se lembrar, quando
this.say
é criado, ele (como todas as funções) recebe uma referência internathis.say.[[Scope]]
para o LexicalEnvironment atual. Portanto, o LexicalEnvironment da execução atual do usuário permanece na memória. Todas as variáveis de User também são suas propriedades, portanto, elas também são mantidas com cuidado, e não são descartadas normalmente.O objetivo é garantir que, se a função interna quiser acessar uma variável externa no futuro, ela será capaz de fazê-lo.
Para resumir:
Isso é chamado de encerramento.
fonte
As funções JavaScript podem acessar:
Se uma função acessa seu ambiente, a função é um fechamento.
Observe que funções externas não são necessárias, embora ofereçam benefícios que não discuto aqui. Ao acessar dados em seu ambiente, um fechamento mantém esses dados ativos. Na subcasa das funções externas / internas, uma função externa pode criar dados locais e, eventualmente, sair, e, no entanto, se alguma função interna sobreviver após a saída da função externa, as funções internas manterão os dados locais da função externa vivo.
Exemplo de um fechamento que usa o ambiente global:
Imagine que os eventos de botão Votação para cima e Votação para pilha excedente sejam implementados como closures, voteUp_click e voteDown_click, que têm acesso a variáveis externas isVotedUp e isVotedDown, definidas globalmente. (Por uma questão de simplicidade, estou me referindo aos botões de votação de perguntas do StackOverflow, não à matriz de botões de votação de resposta.)
Quando o usuário clica no botão VoteUp, a função voteUp_click verifica se isVotedDown == true para determinar se é necessário votar ou simplesmente cancelar uma votação para baixo. A função voteUp_click é um fechamento porque está acessando seu ambiente.
Todas essas quatro funções são encerramentos, pois todos acessam seu ambiente.
fonte
Como pai de uma criança de 6 anos, atualmente ensina crianças pequenas (e um novato em relação à codificação sem educação formal, para que sejam necessárias correções), acho que a lição seria melhor através de brincadeiras práticas. Se a criança de 6 anos está pronta para entender o que é um fechamento, então ela tem idade suficiente para tentar. Eu sugiro colar o código no jsfiddle.net, explicando um pouco e deixando-os sozinhos para criar uma música única. O texto explicativo abaixo é provavelmente mais apropriado para uma criança de 10 anos.
INSTRUÇÕES
DADOS: Os dados são uma coleção de fatos. Pode ser números, palavras, medidas, observações ou até apenas descrições de coisas. Você não pode tocar, cheirar ou provar. Você pode escrever, falar e ouvir. Você poderia usá-lo para criar cheiro e sabor toque usando um computador. Pode ser útil por um computador usando código.
CÓDIGO: Toda a escrita acima é chamada de código . Está escrito em JavaScript.
JAVASCRIPT: JavaScript é uma linguagem. Como inglês, francês ou chinês são idiomas. Existem muitos idiomas que são entendidos por computadores e outros processadores eletrônicos. Para que o JavaScript seja entendido por um computador, é necessário um intérprete. Imagine se um professor que só fala russo vem ensinar sua classe na escola. Quando o professor disser "садятся", a turma não entenderá. Mas, felizmente, você tem um aluno russo em sua classe que diz a todos que isso significa "todos se sentam" - assim todos vocês. A turma é como um computador e o aluno russo é o intérprete. Para JavaScript, o intérprete mais comum é chamado de navegador.
NAVEGADOR: Quando você se conecta à Internet em um computador, tablet ou telefone para visitar um site, usa um navegador. Exemplos que você pode conhecer são o Internet Explorer, Chrome, Firefox e Safari. O navegador pode entender o JavaScript e informar ao computador o que ele precisa fazer. As instruções JavaScript são chamadas de funções.
FUNÇÃO: Uma função em JavaScript é como uma fábrica. Pode ser uma pequena fábrica com apenas uma máquina dentro. Ou pode conter muitas outras pequenas fábricas, cada uma com muitas máquinas fazendo trabalhos diferentes. Em uma fábrica de roupas da vida real, pode haver resmas de tecido e bobinas de fio entrando e camisetas e jeans saindo. Nossa fábrica JavaScript processa apenas dados, não pode costurar, perfurar ou derreter metais. Em nossa fábrica de JavaScript, os dados entram e saem.
Todo esse material de dados parece um pouco chato, mas é realmente muito legal; podemos ter uma função que diga ao robô o que fazer para o jantar. Digamos que eu convide você e seu amigo para minha casa. Você gosta mais de coxas de frango, gosto de salsichas, seu amigo sempre quer o que você quer e meu amigo não come carne.
Como não tenho tempo para fazer compras, a função precisa saber o que temos na geladeira para tomar decisões. Cada ingrediente tem um tempo de cozimento diferente e queremos que tudo seja servido quente pelo robô ao mesmo tempo. Precisamos fornecer à função os dados sobre o que gostamos, a função pode 'conversar' com a geladeira e a função pode controlar o robô.
Uma função normalmente tem um nome, parênteses e chaves. Como isso:
Observe que
/*...*/
e//
pare o código que está sendo lido pelo navegador.NOME: Você pode chamar uma função sobre qualquer palavra que desejar. O exemplo "cookMeal" é típico ao unir duas palavras e fornecer à segunda uma letra maiúscula no início - mas isso não é necessário. Ele não pode ter um espaço e não pode ser um número por si só.
PAIS: "Parênteses" ou
()
são a caixa de correio na porta da fábrica da função JavaScript ou uma caixa postal na rua para enviar pacotes de informações à fábrica. Às vezes, a caixa postal pode ser marcada, por exemplocookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, nesse caso, você sabe quais dados você precisa fornecer.Suspensórios: "Suspensórios" que se parecem com isso
{}
são as janelas coloridas de nossa fábrica. De dentro da fábrica, você pode ver, mas de fora, não pode ver.O EXEMPLO DE CÓDIGO LONGO ACIMA
Nosso código começa com a palavra função , então sabemos que é uma! Então o nome da função canta - essa é a minha própria descrição do que é a função. Então parênteses () . Os parênteses estão sempre lá para uma função. Às vezes, eles estão vazios, e às vezes eles têm algo em Este tem uma palavra em.:
(person)
. Depois disso, existe uma chave assim{
. Isso marca o início da função sing () . Tem um parceiro que marca o final de cantar () como este}
Portanto, essa função pode ter algo a ver com o canto e pode precisar de alguns dados sobre uma pessoa. Possui instruções internas para fazer algo com esses dados.
Agora, após a função cantar () , perto do final do código está a linha
VARIÁVEL: As letras var representam "variável". Uma variável é como um envelope. Do lado de fora, este envelope está marcado como "pessoa". No interior, ele contém um pedaço de papel com as informações de que nossa função precisa, algumas letras e espaços unidos como um pedaço de barbante (chamado de barbante) que faz a frase "uma velha senhora". Nosso envelope pode conter outros tipos de coisas como números (chamados números inteiros), instruções (chamadas funções), listas (chamadas matrizes ). Como essa variável é gravada fora de todos os chavetas
{}
, e porque você pode ver através das janelas coloridas quando estiver dentro das chavetas, essa variável pode ser vista de qualquer lugar no código. Chamamos isso de 'variável global'.VARIÁVEL GLOBAL: pessoa é uma variável global, o que significa que se você alterar seu valor de "uma velha senhora" para "um jovem", a pessoa continuará sendo jovem até que você decida alterá-la novamente e que qualquer outra função em o código pode ver que é um jovem. Pressione o F12botão ou veja as configurações de Opções para abrir o console do desenvolvedor de um navegador e digite "pessoa" para ver qual é esse valor. Digite
person="a young man"
para alterá-lo e digite "pessoa" novamente para ver se foi alterado.Depois disso, temos a linha
Esta linha está chamando a função, como se estivesse chamando um cachorro
Quando o navegador carrega o código JavaScript e atinge essa linha, ele inicia a função. Coloquei a linha no final para garantir que o navegador tenha todas as informações necessárias para executá-lo.
Funções definem ações - a principal função é cantar. Ele contém uma variável chamada firstPart que se aplica ao canto da pessoa que se aplica a cada um dos versos da música: "Havia" + pessoa + "que engoliu". Se você digitar firstPart no console, não receberá resposta porque a variável está bloqueada em uma função - o navegador não pode ver dentro das janelas coloridas dos aparelhos.
FECHAMENTOS: Os fechamentos são as funções menores que estão dentro da função big sing () . As pequenas fábricas dentro da grande fábrica. Cada um tem seu próprio aparelho, o que significa que as variáveis dentro deles não podem ser vistas de fora. É por isso que os nomes das variáveis ( criatura e resultado ) podem ser repetidos nos fechamentos, mas com valores diferentes. Se você digitar esses nomes de variáveis na janela do console, não obterá seu valor porque está oculto por duas camadas de janelas coloridas.
Todos os fechamentos sabem qual é a variável da função sing () chamada firstPart , porque eles podem ver pelas janelas coloridas.
Após os fechamentos vêm as linhas
A função sing () chama cada uma dessas funções na ordem em que são dadas. Em seguida, o trabalho da função sing () será concluído.
fonte
Ok, conversando com uma criança de 6 anos, eu usaria as seguintes associações.
Compare com uma situação em que uma porta foi trancada por correntes de ar e ninguém dentro (execução da função geral); em seguida, algum incêndio local ocorre e incendeia a sala (coletor de lixo: D); em seguida, uma nova sala foi construída e agora você pode sair outro brinquedos lá (nova instância de função), mas nunca obtenha os mesmos brinquedos que foram deixados na primeira instância da sala.
Para uma criança avançada, eu colocaria algo como o seguinte. Não é perfeito, mas faz você se sentir sobre o que é:
Como você pode ver, os brinquedos deixados na sala ainda são acessíveis pelo irmão e não importa se a sala está trancada. Aqui está um jsbin para brincar com ele.
fonte
Uma resposta para um garoto de seis anos (supondo que ele saiba o que é uma função, o que é uma variável e quais são os dados):
Funções podem retornar dados. Um tipo de dados que você pode retornar de uma função é outra função. Quando essa nova função é retornada, todas as variáveis e argumentos usados na função que a criou não desaparecem. Em vez disso, essa função pai "fecha". Em outras palavras, nada pode olhar dentro dele e ver as variáveis que ele usou, exceto a função que retornou. Essa nova função tem uma capacidade especial de olhar para trás na função que a criou e ver os dados nela.
Outra maneira realmente simples de explicar é em termos de escopo:
Sempre que você criar um escopo menor dentro de um escopo maior, o escopo menor sempre poderá ver o que está no escopo maior.
fonte
Uma função em JavaScript não é apenas uma referência a um conjunto de instruções (como na linguagem C), mas também inclui uma estrutura de dados oculta que é composta de referências a todas as variáveis não-locais que ele usa (variáveis capturadas). Essas funções de duas peças são chamadas de fechamentos. Toda função em JavaScript pode ser considerada um fechamento.
Encerramentos são funções com um estado. É um pouco semelhante a "this" no sentido de que "this" também fornece estado para uma função, mas function e "this" são objetos separados ("this" é apenas um parâmetro sofisticado e a única maneira de vinculá-lo permanentemente a um função é criar um fechamento). Enquanto "this" e função sempre vivem separadamente, uma função não pode ser separada de seu fechamento e o idioma não fornece meios para acessar variáveis capturadas.
Como todas essas variáveis externas referenciadas por uma função lexicamente aninhada são na verdade variáveis locais na cadeia de suas funções anexas lexicamente (variáveis globais podem ser consideradas variáveis locais de alguma função raiz), e toda execução de uma função cria novas instâncias de suas variáveis locais, segue-se que toda execução de uma função retornando (ou transferindo-a, como registrando-a como um retorno de chamada) uma função aninhada cria um novo fechamento (com seu próprio conjunto potencialmente exclusivo de variáveis não-locais referenciadas que representam sua execução contexto).
Além disso, deve-se entender que variáveis locais em JavaScript são criadas não no quadro da pilha, mas na pilha e destruídas apenas quando ninguém as está referenciando. Quando uma função retorna, as referências a suas variáveis locais são decrementadas, mas ainda podem ser nulas se, durante a execução atual, se tornarem parte de um fechamento e ainda são referenciadas por suas funções lexicamente aninhadas (o que pode acontecer apenas se as referências a essas funções aninhadas foram retornadas ou transferidas para algum código externo).
Um exemplo:
fonte
Talvez um pouco além de todos, exceto o mais precoce dos seis anos, mas alguns exemplos que ajudaram a tornar o conceito de fechamento em JavaScript um clique para mim.
Um fechamento é uma função que tem acesso ao escopo de outra função (suas variáveis e funções). A maneira mais fácil de criar um fechamento é com uma função dentro de uma função; a razão é que, em JavaScript, uma função sempre tem acesso ao escopo da função que a contém.
ALERTA: macaco
No exemplo acima, chama-se outerFunction, que por sua vez chama innerFunction. Observe como outerVar está disponível para innerFunction, evidenciado pelo alerta correto do valor de outerVar.
Agora considere o seguinte:
ALERTA: macaco
referenceToInnerFunction é definido como outerFunction (), que simplesmente retorna uma referência a innerFunction. Quando referenceToInnerFunction é chamado, ele retorna outerVar. Novamente, como acima, isso demonstra que innerFunction tem acesso a outerVar, uma variável de outerFunction. Além disso, é interessante notar que ele mantém esse acesso mesmo após a execução da outerFunction.
E é aqui que as coisas ficam realmente interessantes. Se nos livrarmos de outerFunction, digamos defini-lo como nulo, você pode pensar que referenceToInnerFunction perderia seu acesso ao valor de outerVar. Mas esse não é o caso.
ALERTA: macaco ALERTA: macaco
Mas como é isso? Como referenceToInnerFunction ainda pode saber o valor de outerVar agora que outerFunction foi definido como nulo?
A razão pela qual referenceToInnerFunction ainda pode acessar o valor de outerVar é porque, quando o fechamento foi criado pela primeira vez, colocando innerFunction dentro de outerFunction, innerFunction adicionou uma referência ao escopo do outerFunction (suas variáveis e funções) à sua cadeia de escopos. O que isso significa é que innerFunction possui um ponteiro ou referência a todas as variáveis de outerFunction, incluindo outerVar. Portanto, mesmo quando o outerFunction terminar de executar, ou mesmo se ele for excluído ou definido como nulo, as variáveis em seu escopo, como outerVar, permanecem na memória por causa da excelente referência a eles na parte do innerFunction que foi retornada para referenceToInnerFunction. Para liberar verdadeiramente a outerVar e o restante das variáveis da outerFunction da memória, você precisaria se livrar dessa excelente referência a elas,
//////////
Duas outras coisas sobre fechamentos a serem observadas. Primeiro, o fechamento sempre terá acesso aos últimos valores de sua função de contenção.
ALERTA: gorila
Segundo, quando um fechamento é criado, ele mantém uma referência a todas as variáveis e funções de sua função envolvente; não consegue escolher. E, portanto, os fechamentos devem ser usados com moderação ou pelo menos com cuidado, pois podem consumir muita memória; muitas variáveis podem ser mantidas na memória muito tempo após a conclusão da execução de uma função de contenção.
fonte
Eu simplesmente apontaria para a página Mozilla Closures . É a melhor, mais concisa e simples explicação dos princípios básicos do fechamento e do uso prático que eu encontrei. É altamente recomendado para quem aprende JavaScript.
E sim, eu recomendaria até para uma criança de 6 anos - se a criança de 6 anos estiver aprendendo sobre fechamentos, é lógico que eles estão prontos para compreender a explicação concisa e simples fornecida no artigo.
fonte