O que é um 'fechamento'?


432

Fiz uma pergunta sobre curry e os fechamentos foram mencionados. O que é um fechamento? Como isso se relaciona com o curry?


22
Agora, o que exatamente é o fechamento ??? Algumas respostas dizem que o fechamento é a função. Alguns dizem que é a pilha. Algumas respostas dizem que é o valor "oculto". No meu entender, é a função + variáveis ​​incluídas.
Roland

3
Explica o que um fechamento é: stackoverflow.com/questions/4103750/...
dietbuddha

Veja também O que é um fechamento? em softwareengineering.stackexchange
B12Toaster

Explica o que é um fechamento e o caso de uso comum: trungk18.com/experience/javascript-closure
Sasuke91

Respostas:


744

Escopo variável

Quando você declara uma variável local, essa variável tem um escopo. Geralmente, as variáveis ​​locais existem apenas dentro do bloco ou função em que você as declara.

function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

Se eu tentar acessar uma variável local, a maioria dos idiomas procurará por ela no escopo atual, passando pelos escopos principais até atingirem o escopo raiz.

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

Quando um bloco ou função termina, suas variáveis ​​locais não são mais necessárias e geralmente ficam sem memória.

É assim que normalmente esperamos que as coisas funcionem.

Um fechamento é um escopo variável local persistente

Um fechamento é um escopo persistente que se apega a variáveis ​​locais, mesmo após a execução do código sair desse bloco. Os idiomas que suportam o fechamento (como JavaScript, Swift e Ruby) permitirão que você mantenha uma referência a um escopo (incluindo seus escopos pai), mesmo após a conclusão da execução do bloco em que essas variáveis ​​foram declaradas, desde que você mantenha uma referência para esse bloco ou função em algum lugar.

O objeto de escopo e todas as suas variáveis ​​locais estão vinculadas à função e persistirão enquanto essa função persistir.

Isso nos dá portabilidade de função. Podemos esperar que todas as variáveis ​​que estavam no escopo quando a função foi definida pela primeira vez ainda estejam no escopo quando mais tarde chamarmos a função, mesmo se a chamarmos em um contexto completamente diferente.

Por exemplo

Aqui está um exemplo muito simples em JavaScript que ilustra o ponto:

outer = function() {
  var a = 1;
  var inner = function() {
    console.log(a);
  }
  return inner; // this returns a function
}

var fnc = outer(); // execute outer to get inner 
fnc();

Aqui eu defini uma função dentro de uma função. A função interna obtém acesso a todas as variáveis ​​locais da função externa, inclusive a. A variável aestá no escopo da função interna.

Normalmente, quando uma função sai, todas as suas variáveis ​​locais são exageradas. No entanto, se retornarmos a função interna e a atribuirmos a uma variável fncpara que ela persista após a outersaída, todas as variáveis ​​que estavam no escopo quando innerdefinidas também persistem . A variável afoi encerrada - está dentro de um fechamento.

Observe que a variável aé totalmente privada para fnc. Essa é uma maneira de criar variáveis ​​privadas em uma linguagem de programação funcional como o JavaScript.

Como você pode adivinhar, quando eu chamo fnc(), imprime o valor de a, que é "1".

Em um idioma sem fechamento, a variável ateria sido coletada e descartada como lixo quando a função fosse outerencerrada. Chamar fnc geraria um erro porque anão existe mais.

Em JavaScript, a variável apersiste porque o escopo da variável é criado quando a função é declarada pela primeira vez e persiste enquanto a função continuar a existir.

apertence ao escopo de outer. O escopo de innerpossui um ponteiro pai para o escopo de outer. fncé uma variável que aponta para inner. apersiste enquanto fncpersiste. aestá dentro do fechamento.


116
Eu pensei que este era um exemplo muito bom e fácil de entender.
user12345613

16
Obrigado pela explicação incrível, eu já vi muitos, mas este é o momento em que realmente entendi.
Dimitar Dimitrov

2
Eu poderia ter um exemplo de como isso funciona em uma biblioteca como o JQuery, conforme indicado no segundo ao último parágrafo? Eu não entendi isso totalmente.
DPM

6
Olá Jubbat, sim, abra o jquery.js e dê uma olhada na primeira linha. Você verá uma função sendo aberta. Agora pule para o final, você verá window.jQuery = window. $ = JQuery. Então a função é fechada e executada automaticamente. Agora você tem acesso à função $, que por sua vez tem acesso às outras funções definidas no fechamento. Isso responde à sua pergunta?
superluminary

4
Melhor explicação na web. Maneira mais simples do que eu pensava
Mantis

95

Vou dar um exemplo (em JavaScript):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

O que essa função, makeCounter, faz é retornar uma função, que chamamos de x, que será contada uma vez cada vez que for chamada. Como não fornecemos nenhum parâmetro para x, ele deve se lembrar da contagem. Ele sabe onde encontrá-lo com base no que é chamado de escopo lexical - ele deve procurar o local onde está definido para encontrar o valor. Esse valor "oculto" é chamado de encerramento.

Aqui está o meu exemplo de curry novamente:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

O que você pode ver é que, quando você chama add com o parâmetro a (que é 3), esse valor está contido no fechamento da função retornada que estamos definindo como add3. Dessa forma, quando chamamos add3, ele sabe onde encontrar o valor a para realizar a adição.


4
IDK, qual idioma (provavelmente F #) você usou no idioma acima. Poderia, por favor, dar o exemplo acima em pseudocódigo? Estou tendo dificuldade para entender isso.
usuário


3
@KyleCronin Ótimo exemplo, obrigado. P: É mais correto dizer "o valor oculto é chamado de fechamento" ou "a função que oculta o valor é o fechamento"? Ou "o processo de ocultar o valor é o fechamento"? Obrigado!

2
@RobertHume Good question. Semanticamente, o termo "fechamento" é um tanto ambíguo. Minha definição pessoal é que a combinação do valor oculto e do uso da função envolvente constitui o fechamento.
Kyle Cronin

1
@KyleCronin Obrigado - Eu tenho um esquema a meio do período na segunda-feira. :) Queria ter o conceito de "fechamento" sólido na minha cabeça. Obrigado por postar esta ótima resposta à pergunta do OP!

58

A resposta de Kyle é muito boa. Eu acho que o único esclarecimento adicional é que o fechamento é basicamente um instantâneo da pilha no momento em que a função lambda é criada. Então, quando a função é reexecutada, a pilha é restaurada para esse estado antes de executar a função. Assim, como Kyle menciona, esse valor oculto ( count) está disponível quando a função lambda é executada.


14
Não é apenas a pilha - são os escopos lexicais anexos que são preservados, independentemente de estarem armazenados na pilha ou na pilha (ou em ambos).
Matt Fenwick

38

Antes de tudo, ao contrário do que muitas pessoas aqui dizem, o fechamento não é uma função ! Então o que é isso?
É um conjunto de símbolos definidos no "contexto circundante" de uma função (conhecido como ambiente ) que a torna uma expressão FECHADA (ou seja, uma expressão na qual todo símbolo é definido e tem um valor, para que possa ser avaliado).

Por exemplo, quando você tem uma função JavaScript:

function closed(x) {
  return x + 3;
}

é uma expressão fechada porque todos os símbolos que nele ocorrem são definidos (seus significados são claros), para que você possa avaliá-lo. Em outras palavras, é independente .

Mas se você tem uma função como esta:

function open(x) {
  return x*y + 3;
}

é uma expressão aberta porque há símbolos nela que não foram definidos nela. Ou seja y,. Ao olhar para essa função, não podemos dizer o que yé e o que isso significa, não sabemos seu valor, portanto, não podemos avaliar essa expressão. Ou seja, não podemos chamar essa função até dizermos o que ydeveria significar nela. Isso yé chamado de variável livre .

Isso yimplora por uma definição, mas essa definição não faz parte da função - ela é definida em outro lugar, em seu "contexto circundante" (também conhecido como ambiente ). Pelo menos é o que esperamos: P

Por exemplo, ele pode ser definido globalmente:

var y = 7;

function open(x) {
  return x*y + 3;
}

Ou pode ser definido em uma função que a envolve:

var global = 2;

function wrapper(y) {
  var w = "unused";

  return function(x) {
    return x*y + 3;
  }
}

A parte do ambiente que dá às variáveis ​​livres de uma expressão seus significados é o fechamento . É chamado assim, porque transforma uma expressão aberta em uma fechada , fornecendo essas definições ausentes para todas as suas variáveis ​​livres , para que possamos avaliá-la.

No exemplo acima, a função interna (que não demos um nome porque não precisamos dela) é uma expressão aberta porque a variável yé livre - sua definição está fora da função, na função que a envolve . O ambiente para essa função anônima é o conjunto de variáveis:

{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Agora, o fechamento é a parte desse ambiente que fecha a função interna, fornecendo as definições para todas as suas variáveis ​​livres . No nosso caso, a única variável livre na função interna era y, portanto, o fechamento dessa função é esse subconjunto de seu ambiente:

{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Os outros dois símbolos definidos no ambiente não fazem parte do fechamento dessa função, porque não exige que eles sejam executados. Eles não são necessários para fechá- lo.

Mais sobre a teoria por trás disso aqui: https://stackoverflow.com/a/36878651/434562

Vale ressaltar que no exemplo acima, a função wrapper retorna sua função interna como um valor. O momento em que chamamos essa função pode ser remoto no tempo a partir do momento em que a função foi definida (ou criada). Em particular, sua função de quebra automática não está mais em execução e seus parâmetros que estão na pilha de chamadas não estão mais lá: P Isso causa um problema, porque a função interna precisa yestar lá quando é chamada! Em outras palavras, requer que as variáveis ​​do seu fechamento sobrevivam de alguma forma à função do wrapper e estejam lá quando necessário. Portanto, a função interna precisa fazer um instantâneo dessas variáveis ​​que tornam seu fechamento e armazená-las em algum lugar seguro para uso posterior. (Em algum lugar fora da pilha de chamadas.)

E é por isso que as pessoas freqüentemente confundem o termo encerramento como aquele tipo especial de função que pode fazer instantâneos das variáveis ​​externas que eles usam, ou da estrutura de dados usada para armazenar essas variáveis ​​para mais tarde. Mas espero que você entenda agora que eles não são o fechamento propriamente dito - são apenas maneiras de implementar fechamentos em uma linguagem de programação ou mecanismos de linguagem que permitem que as variáveis ​​do fechamento da função estejam lá quando necessário. Existem muitos conceitos errados sobre fechamentos que (desnecessariamente) tornam esse assunto muito mais confuso e complicado do que realmente é.


1
Uma analogia que pode ajudar os iniciantes a isso é um fechamento que amarra todas as pontas soltas , que é o que uma pessoa faz quando busca o fechamento (ou resolve todas as referências necessárias, ou ...). Bem, isso me ajudou a pensar dessa maneira: o)
Will Crawford

Li várias definições de fechamento ao longo dos anos, mas acho que essa é a minha favorita até agora. Eu acho que todos nós temos nosso próprio modo de mapear mentalmente conceitos como este e este aqui brinca muito com o meu.
Jason S.

29

Um fechamento é uma função que pode referenciar o estado em outra função. Por exemplo, em Python, isso usa o fechamento "inner":

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1

23

Para ajudar a facilitar o entendimento dos fechamentos, pode ser útil examinar como eles podem ser implementados em uma linguagem processual. Esta explicação seguirá uma implementação simplista de fechamentos no esquema.

Para começar, devo introduzir o conceito de um espaço para nome. Quando você insere um comando em um intérprete de esquema, ele deve avaliar os vários símbolos na expressão e obter seu valor. Exemplo:

(define x 3)

(define y 4)

(+ x y) returns 7

As expressões define armazenam o valor 3 no local para x e o valor 4 no local para y. Então, quando chamamos (+ xy), o intérprete pesquisa os valores no espaço para nome e pode executar a operação e retornar 7.

No entanto, no esquema, existem expressões que permitem substituir temporariamente o valor de um símbolo. Aqui está um exemplo:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

O que a palavra-chave let faz é introduzir um novo espaço para nome com x como o valor 5. Você perceberá que ainda é possível ver que y é 4, fazendo com que a soma retornada seja 9. Você também pode ver que quando a expressão termina x voltou a ser 3. Nesse sentido, x foi temporariamente mascarado pelo valor local.

Linguagens procedurais e orientadas a objetos têm um conceito semelhante. Sempre que você declara uma variável em uma função que tem o mesmo nome que uma variável global, obtém o mesmo efeito.

Como implementaríamos isso? Uma maneira simples é com uma lista vinculada - a cabeça contém o novo valor e a cauda contém o antigo espaço para nome. Quando você precisa procurar um símbolo, começa na cabeça e desce pela cauda.

Agora vamos pular para a implementação de funções de primeira classe no momento. Mais ou menos, uma função é um conjunto de instruções a serem executadas quando a função é chamada culminando no valor de retorno. Quando lemos uma função, podemos armazenar essas instruções nos bastidores e executá-las quando a função é chamada.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

Definimos x como 3 e mais-x como seu parâmetro, y, mais o valor de x. Finalmente, chamamos plus-x em um ambiente em que x foi mascarado por um novo x, este valorizado 5. Se apenas armazenarmos a operação (+ xy), para a função plus-x, já que estamos no contexto de x sendo 5, o resultado retornado seria 9. Isso é chamado de escopo dinâmico.

No entanto, Scheme, Common Lisp e muitas outras linguagens têm o que se chama escopo lexical - além de armazenar a operação (+ xy), também armazenamos o espaço para nome nesse ponto específico. Dessa forma, quando procuramos os valores, podemos ver que x, nesse contexto, é realmente 3. Esse é um fechamento.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

Em resumo, podemos usar uma lista vinculada para armazenar o estado do espaço para nome no momento da definição da função, permitindo acessar variáveis ​​de escopos anexos, além de fornecer a capacidade de mascarar localmente uma variável sem afetar o restante do programa.


ok, graças à sua resposta, acho que finalmente tenho uma ideia do que é o fechamento. Mas há uma grande questão: "podemos usar uma lista vinculada para armazenar o estado do espaço para nome no momento da definição da função, permitindo acessar variáveis ​​que, de outra forma, não estariam mais no escopo". Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
Lazer 23/05

@ Laser: Desculpe, essa frase não fazia muito sentido, então eu a atualizei. Espero que faça mais sentido agora. Além disso, não pense na lista vinculada como um detalhe de implementação (como é muito ineficiente), mas como uma maneira simples de conceituar como isso poderia ser feito.
Kyle Cronin

10

Aqui está um exemplo do mundo real do porquê Closures kick ass ... Isso é direto do meu código Javascript. Deixe-me ilustrar.

Function.prototype.delay = function(ms /*[, arg...]*/) {
  var fn = this,
      args = Array.prototype.slice.call(arguments, 1);

  return window.setTimeout(function() {
      return fn.apply(fn, args);
  }, ms);
};

E aqui está como você o usaria:

var startPlayback = function(track) {
  Player.play(track);  
};
startPlayback(someTrack);

Agora imagine que você deseja que a reprodução comece atrasada, como, por exemplo, 5 segundos depois que esse trecho de código for executado. Bem, isso é fácil delaye seu fechamento:

startPlayback.delay(5000, someTrack);
// Keep going, do other things

Quando você chama delaycom 5000ms, o primeiro trecho é executado e armazena os argumentos passados ​​em seu fechamento. Então, 5 segundos depois, quando o setTimeoutretorno de chamada acontece, o fechamento ainda mantém essas variáveis, para que ele possa chamar a função original com os parâmetros originais.
Este é um tipo de curry ou decoração funcional.

Sem encerramentos, você teria que, de alguma forma, manter o estado dessas variáveis ​​fora da função, desarrumando o código fora da função com algo que pertence logicamente a ela. O uso de fechamentos pode melhorar muito a qualidade e a legibilidade do seu código.


1
Note-se que a extensão de linguagem ou de acolhimento objetos é geralmente considerado uma coisa ruim como eles são parte do namespace global
Jon Cooke

9

Funções que não contêm variáveis ​​livres são chamadas de funções puras.

Funções que contêm uma ou mais variáveis ​​livres são chamadas de fechamentos.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo 
  // foo is a free variable from the outer environment
}

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure


Por que isso é menosprezado? Na verdade, é muito mais "no caminho certo" com essa distinção entre variáveis ​​livres e variáveis ​​vinculadas e funções puras / fechadas e funções impuras / abertas, do que a maioria das outras respostas sem noção aqui: P (descontando confusões de fechamento com funções sendo fechado).
SasQ

Eu realmente não tenho idéia. É por isso que o StackOverflow é péssimo. Basta olhar para a fonte da minha resposta. Quem poderia argumentar com isso?
soundyogi

SO não é péssimo e eu nunca ouvi falar do termo "variável livre"
Kai

É difícil falar sobre fechamentos sem mencionar variáveis ​​livres. Basta procurá-los. Terminologia CS padrão.
ComDubh

"Funções que contêm uma ou mais variáveis ​​livres são chamadas de fechamentos" não é uma definição correta - fechamentos são sempre objetos de primeira classe.
ComDubh

7

tl; dr

Um fechamento é uma função e seu escopo atribuído a (ou usado como) uma variável. Assim, o fechamento do nome: o escopo e a função são incluídos e usados ​​como qualquer outra entidade.

Explicação detalhada do estilo Wikipedia

Segundo a Wikipedia, um fechamento é:

Técnicas para implementar a vinculação de nomes com escopo lexical em idiomas com funções de primeira classe.

O que isso significa? Vamos analisar algumas definições.

Explicarei os fechamentos e outras definições relacionadas usando este exemplo:

function startAt(x) {
    return function (y) {
        return x + y;
    }
}

var closure1 = startAt(1);
var closure2 = startAt(5);

console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)

Funções de primeira classe

Basicamente, isso significa que podemos usar funções como qualquer outra entidade . Podemos modificá-los, passá-los como argumentos, retorná-los de funções ou atribuí-los a variáveis. Tecnicamente falando, eles são cidadãos de primeira classe , daí o nome: funções de primeira classe.

No exemplo acima, startAtretorna uma função ( anônima ) à qual a função é atribuída a closure1e closure2. Então, como você vê, o JavaScript trata as funções como qualquer outra entidade (cidadãos de primeira classe).

Ligação de nome

A associação de nomes é descobrir quais dados uma variável (identificador) faz referência . O escopo é realmente importante aqui, pois é isso que determinará como uma ligação é resolvida.

No exemplo acima:

  • No escopo da função anônima interna, yé vinculado a 3.
  • No startAtescopo do, xestá vinculado a 1ou 5(dependendo do fechamento).

Dentro do escopo da função anônima, xnão está vinculado a nenhum valor; portanto, ele precisa ser resolvido no startAtescopo superior ( s).

Escopo lexical

Como a Wikipedia diz , o escopo:

É a região de um programa de computador em que a ligação é válida: onde o nome pode ser usado para se referir à entidade .

Existem duas técnicas:

  • Escopo lexical (estático): a definição de uma variável é resolvida pesquisando seu bloco ou função que contém, se isso falhar na pesquisa do bloco que contém o exterior, e assim por diante.
  • Escopo dinâmico: a função de chamada é pesquisada, depois a função que chamou essa função de chamada e assim por diante, progredindo na pilha de chamadas.

Para mais explicações, confira esta pergunta e dê uma olhada na Wikipedia .

No exemplo acima, podemos ver que o JavaScript tem um escopo lexical, porque quando xé resolvida, a ligação é pesquisada no startAtescopo superior ( s), com base no código-fonte (a função anônima que procura x é definida por dentro startAt) e não com base na pilha de chamadas, a maneira (o escopo em que) a função foi chamada.

Empacotando (fechando)

Em nosso exemplo, quando chamamos startAt, ele retornará uma função (de primeira classe) que será atribuída closure1e, closure2assim, um fechamento será criado, porque as variáveis ​​passadas 1e 5serão salvas no startAtescopo do que será incluído com o retorno função anônima. Quando chamamos essa função anônima via closure1e closure2com o mesmo argumento ( 3), o valor de yserá encontrado imediatamente (como esse é o parâmetro dessa função), mas xnão está vinculado ao escopo da função anônima, portanto a resolução continua em o escopo da função superior (lexicamente) (que foi salvo no fechamento), onde xestá associado a um 1ou5. Agora sabemos tudo sobre o somatório para que o resultado possa ser retornado e impresso.

Agora você deve entender os fechamentos e como eles se comportam, o que é uma parte fundamental do JavaScript.

Escovando

Ah, e você também aprendeu do que se trata o curry : você usa funções (fechamentos) para passar cada argumento de uma operação em vez de usar uma função com vários parâmetros.


5

Closure é um recurso no JavaScript em que uma função tem acesso a suas próprias variáveis ​​de escopo, acesso às variáveis ​​de função externas e acesso às variáveis ​​globais.

O fechamento tem acesso ao seu escopo de função externa, mesmo após o retorno da função externa. Isso significa que um fechamento pode lembrar e acessar variáveis ​​e argumentos de sua função externa, mesmo após a conclusão da função.

A função interna pode acessar as variáveis ​​definidas em seu próprio escopo, o escopo da função externa e o escopo global. E a função externa pode acessar a variável definida em seu próprio escopo e no escopo global.

Exemplo de fechamento :

var globalValue = 5;

function functOuter() {
  var outerFunctionValue = 10;

  //Inner function has access to the outer function value
  //and the global variables
  function functInner() {
    var innerFunctionValue = 5;
    alert(globalValue + outerFunctionValue + innerFunctionValue);
  }
  functInner();
}
functOuter();  

A saída será 20, que soma da variável própria da função interna, variável da função externa e valor da variável global.


4

Em uma situação normal, as variáveis ​​são vinculadas pela regra de escopo: Variáveis ​​locais funcionam apenas dentro da função definida. O fechamento é uma maneira de quebrar esta regra temporariamente por conveniência.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

no código acima, lambda(|n| a_thing * n}é o fechamento porque a_thingé referido pelo lambda (um criador de função anônimo).

Agora, se você colocar a função anônima resultante em uma variável de função.

foo = n_times(4)

foo quebrará a regra de escopo normal e começará a usar 4 internamente.

foo.call(3)

retorna 12.


2

Em resumo, o ponteiro de função é apenas um ponteiro para um local na base de código do programa (como o contador do programa). Considerando o encerramento = ponteiro de função + quadro de pilha .

.


1

• Um fechamento é um subprograma e o ambiente de referência em que foi definido

- O ambiente de referência é necessário se o subprograma puder ser chamado de qualquer local arbitrário do programa

- Uma linguagem com escopo estático que não permite subprogramas aninhados não precisa de fechamentos

- Os fechamentos são necessários apenas se um subprograma puder acessar variáveis ​​em escopos de aninhamento e puder ser chamado de qualquer lugar

- Para oferecer suporte a fechamentos, uma implementação pode precisar fornecer extensão ilimitada para algumas variáveis ​​(porque um subprograma pode acessar uma variável não local que normalmente não está mais ativa)

Exemplo

function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);

0

Aqui está outro exemplo da vida real, e usando uma linguagem de script popular em jogos - Lua. Eu precisava alterar levemente a maneira como uma função de biblioteca funcionava para evitar um problema com o stdin não estar disponível.

local old_dofile = dofile

function dofile( filename )
  if filename == nil then
    error( 'Can not use default of stdin.' )
  end

  old_dofile( filename )
end

O valor de old_dofile desaparece quando esse bloco de código termina seu escopo (porque é local); no entanto, o valor foi encerrado em um fechamento; portanto, a nova função de dofile redefinida PODE acessá-lo, ou melhor, uma cópia armazenada junto com a função como um 'upvalue'.


0

Do Lua.org :

Quando uma função é escrita incluída em outra função, ela tem acesso total a variáveis ​​locais a partir da função anexa; esse recurso é chamado de escopo lexical. Embora isso possa parecer óbvio, não é. O escopo léxico, além das funções de primeira classe, é um conceito poderoso em uma linguagem de programação, mas poucas linguagens suportam esse conceito.


0

Se você é do mundo Java, pode comparar um fechamento com uma função de membro de uma classe. Veja este exemplo

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

A função gé um fechamento: gfecha a. Portanto, gpode ser comparada com uma função de membro, apode ser comparada com um campo de classe e a função fcom uma classe.


0

Encerramentos Sempre que tivermos uma função definida dentro de outra, a função interna terá acesso às variáveis ​​declaradas na função externa. Os fechamentos são melhor explicados com exemplos. Na Listagem 2-18, você pode ver que a função interna tem acesso a uma variável (variableInOuterFunction) do escopo externo. As variáveis ​​na função externa foram fechadas (ou ligadas) pela função interna. Daí o termo encerramento. O conceito em si é bastante simples e bastante intuitivo.

Listing 2-18:
    function outerFunction(arg) {
     var variableInOuterFunction = arg;

     function bar() {
             console.log(variableInOuterFunction); // Access a variable from the outer scope
     }
     // Call the local function to demonstrate that it has access to arg
     bar(); 
    }
    outerFunction('hello closure!'); // logs hello closure!

fonte: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf


0

Dê uma olhada no código abaixo para entender o fechamento mais profundamente:

        for(var i=0; i< 5; i++){            
            setTimeout(function(){
                console.log(i);
            }, 1000);                        
        }

Aqui o que será produzido? 0,1,2,3,4não que seja 5,5,5,5,5por causa do fechamento

Então, como vai resolver? A resposta está abaixo:

       for(var i=0; i< 5; i++){
           (function(j){     //using IIFE           
                setTimeout(function(){
                               console.log(j);
                           },1000);
            })(i);          
        }

Deixe-me explicar de maneira simples, quando uma função criada nada acontece até que ela chame o loop for no 1º código chamado 5 vezes, mas não seja chamada imediatamente quando chama, ou seja, após 1 segundo e também é assíncrona, portanto antes que o loop for concluído e o valor de armazenamento 5 na var ie finalmente execute a setTimeoutfunção cinco vezes e imprima5,5,5,5,5

Aqui como resolver usando IIFE, isto é, expressão de função de chamada imediata

       (function(j){  //i is passed here           
            setTimeout(function(){
                           console.log(j);
                       },1000);
        })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

Para mais, entenda o contexto de execução para entender o fechamento.

  • Existe mais uma solução para resolver isso usando let (recurso ES6), mas sob o capô a função acima é trabalhada

     for(let i=0; i< 5; i++){           
         setTimeout(function(){
                        console.log(i);
                    },1000);                        
     }
    
    Output: 0,1,2,3,4
    

=> Mais explicações:

Na memória, quando for loop executar imagem faça o seguinte:

Loop 1)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Loop 2)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Loop 3)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Loop 4)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Loop 5)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Aqui eu não sou executado e depois do loop completo, vari armazenou o valor 5 na memória, mas seu escopo é sempre visível na função filhos, então quando a função é executada de dentro setTimeoutpara fora cinco vezes5,5,5,5,5

para resolver esse uso, use IIFE como explicado acima.


obrigado pela sua resposta. seria mais legível se você separasse o código da explicação. (não
recue

0

Currying: permite avaliar parcialmente uma função passando apenas um subconjunto de seus argumentos. Considere isto:

function multiply (x, y) {
  return x * y;
}

const double = multiply.bind(null, 2);

const eight = double(4);

eight == 8;

Encerramento: um fechamento nada mais é do que acessar uma variável fora do escopo de uma função. É importante lembrar que uma função dentro de uma função ou uma função aninhada não é um fechamento. Os fechamentos são sempre usados ​​quando é necessário acessar as variáveis ​​fora do escopo da função.

function apple(x){
   function google(y,z) {
    console.log(x*y);
   }
   google(7,2);
}

apple(3);

// the answer here will be 21

0

O fechamento é muito fácil. Podemos considerar o seguinte: Encerramento = função + seu ambiente lexical

Considere a seguinte função:

function init() {
    var name = “Mozilla”;
}

Qual será o fechamento no caso acima? Função init () e variáveis ​​em seu ambiente lexical, ou seja, nome. Encerramento = init () + nome

Considere outra função:

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

Quais serão os fechamentos aqui? A função interna pode acessar variáveis ​​da função externa. displayName () pode acessar o nome da variável declarada na função pai, init (). No entanto, as mesmas variáveis ​​locais em displayName () serão usadas se existirem.

Encerramento 1: função init + (variável de nome + função displayName ()) -> escopo lexical

Fechamento 2: função displayName + (variável de nome) -> escopo lexical


0

Os fechamentos fornecem estado ao JavaScript.

Estado na programação significa simplesmente lembrar as coisas.

Exemplo

var a = 0;

a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3

No caso acima, o estado é armazenado na variável "a". Em seguida, adicionamos 1 a "a" várias vezes. Só podemos fazer isso porque somos capazes de "lembrar" o valor. O detentor do estado, "a", mantém esse valor na memória.

Freqüentemente, nas linguagens de programação, você deseja acompanhar as coisas, lembrar as informações e acessá-las posteriormente.

Este, em outros idiomas , é comumente realizada através do uso de classes. Uma classe, assim como as variáveis, controla seu estado. E instâncias dessa classe, por sua vez, também têm estado dentro delas. Estado simplesmente significa informações que você pode armazenar e recuperar posteriormente.

Exemplo

class Bread {
  constructor (weight) {
    this.weight = weight;
  }

  render () {
    return `My weight is ${this.weight}!`;
  }
}

Como podemos acessar o "peso" de dentro do método "render"? Bem, graças ao estado. Cada instância da classe Bread pode render seu próprio peso lendo-o no "state", um local na memória onde podemos armazenar essas informações.

Agora, o JavaScript é uma linguagem muito única que, historicamente, não possui classes (mas agora existem apenas funções e variáveis), portanto o Closures fornece uma maneira do JavaScript lembrar as coisas e acessá-las posteriormente.

Exemplo

var n = 0;
var count = function () {
  n = n + 1;
  return n;
};

count(); // # 1
count(); // # 2
count(); // # 3

O exemplo acima alcançou o objetivo de "manter o estado" com uma variável. Isso é ótimo! No entanto, isso tem a desvantagem de que a variável (o detentor do "estado") está agora exposta. Nós podemos fazer melhor. Podemos usar Closures.

Exemplo

var countGenerator = function () {
  var n = 0;
  var count = function () {
    n = n + 1;
    return n;
  };

  return count;
};

var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3

Isto é fantástico.

Agora nossa função "count" pode contar. Só é capaz de fazê-lo porque pode "manter" o estado. O estado neste caso é a variável "n". Esta variável está agora fechada. Fechado no tempo e no espaço. Com o tempo, porque você nunca poderá recuperá-lo, alterá-lo, atribuir um valor ou interagir diretamente com ele. No espaço, porque está aninhado geograficamente na função "countGenerator".

Por que isso é fantástico? Porque, sem envolver nenhuma outra ferramenta sofisticada e complicada (por exemplo, classes, métodos, instâncias, etc.), somos capazes de 1. ocultar 2. o controle à distância

Nós escondemos o estado, a variável "n", o que a torna uma variável privada! Também criamos uma API que pode controlar essa variável de uma maneira predefinida. Em particular, podemos chamar a API como "count ()" e isso adiciona 1 ao "n" de uma "distância". De forma alguma, a forma ou a forma que alguém poderá acessar "n", exceto por meio da API.

O JavaScript é realmente incrível em sua simplicidade.

Os fechamentos são uma grande parte do motivo disso.


0

Um exemplo simples no Groovy para sua referência:

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.