Open ghost opened 5 years ago
Então, esse esquema de função aninhada é conhecido como High-Order function, significa que o retorno da função é outra função, por isso ele retorna um fragmento de função no console.
Fonte: Understanding Higher-Order Functions in JavaScript Tá em inglês, mas é uma boa, ele fala bastante do caso do map,filter e companhia, mas no final fala sobre as customizadas.
O motivo de ele mostrar a função com o nome factor
e não com o 2 é por ser uma função, ele vai mostrar como ela foi escrita, e não como será executada, até porque ela realmente não foi, é um esquema chamado de lazy evaluation, a função só será executada quando ela for realmente necessária, você gerou uma nova função, mas como ela não foi executada, o factor
ainda não foi passado pra ela.
Posso estar muito errado, é o que vejo em algumas linguagens voltadas pro lado funcional, dei uma pesquisada por JS e não achei muito sobre o assunto (específico para as funções), mas creio que seja isso. Segue um artigo falando sobre, em Scala: Non Strict Lazy Evaluations in Functional Programming
Pliavi, resposta relâmpago. Obrigado!
Vou ler os artigos que mandou em seguida, mas antes, compartilho que vi esse programa no livro Eloquent JavaScript, na parte sobre closure. Lá, o autor diz o seguinte:
In the example, multiplier is called and creates an environment in which its factor parameter is bound to 2. The function value it returns, which is stored in twice, remembers this environment. So when that is called, it multiplies its argument by 2.
No exemplo,
multiplier
é chamada e cria um ambiente no qual seu parâmetrofactor
é ligado a2
. O valor de função que retorna, que é armazenado emtwice
, se lembra desse ambiente. Então, quando esse é chamado, multiplica seu argumento por2
.
Multiplier
é chamada somente quando console.log(twice(5));
é executado? Antes, o que está armazenado no twice
? É o retorno da função, o que ele chama de "function value"? Qual é tipo de dado armazenado, é uma função mesmo?
Não exatamente, em console.log(twice(5))
o multiplier
já foi executado retornando a função anônima (arrow function) descrita, tanto que se você alterar a função multiplier, o funcionamento de twice continua o antigo, é como dito no fragmento que você colocou, o ambiente é mantido (lembrado) junto com o número 2, mas só é executado quando twice
é chamado.
Isso, o que está dentro de twice
é uma função, a que multiplier
retorna.
Você pode verificar usando o comando typeof
Ambos, twice
e multiplier
, são funções.
Pior que essas coisas dão um nó na cabeça xD
Veja se entendi, por favor. twice
funciona como uma referência à função aninhada e a seu ambiente? Somente são utilizados quando a função twice
é executada, porque JavaScript é uma linguagem que trabalha com lazy evalutation?
Marquei algumas partes desse fragmento sobre escopo léxico, no Wikipédia, que parecem ter ligação com essa ideia de closure, e com lazy evaluation:
O escopo léxico ou estático foi introduzido pela linguagem ALGOL 60. O escopo é assim denominado, porque pode ser determinado estaticamente, ou seja, antes da execução. O escopo léxico define o escopo em termos da estrutura léxica do programa. Com escopo léxico, um nome sempre se refere ao seu ambiente léxico (mais ou menos) local. Esta é uma propriedade do texto do programa e é feita independente da pilha de chamadas em tempo de execução pela implementação da linguagem. Ou seja, O escopo léxico de uma declaração é a parte do texto do programa, onde a utilização do identificador é uma referência a essa declaração particular do identificador. Pelo fato de esta correspondência só exigir a análise do texto do programa estático, este tipo de delimitação de escopo é também chamado de escopo estático.
Veja se entendi, por favor. twice funciona como uma referência à função aninhada e a seu ambiente?
Pior que tá entrando outro termo técnico aqui... Não é uma referência, pois uma referência é algo (variável) que aponta para um espaço de memória, e mais de uma variável pode ter essa referencia, sendo assim, qaulquer que você alterar, vai alterar as outras, pois todos apontam para o mesmo valor.
Porém o javascript passa tudo por valor, não existe referência, digo, existe, mas apenas para objetos (arrays e funções são objetos), mas não utilizando o operador '=' ou passando como parâmetro de função, sempre será por valor ao invés de referência.
Cada vez que multiplier
é chamada uma nova função anônima é criada e retornada.
No exemplo que você deu, o 2 está lá, mas está implícito apenas.
porque JavaScript é uma linguagem que trabalha com lazy evalutation?
Lazy evaluation não é uma característica da linguagem, é uma estratégia, uma forma de escrever o código para que ele execute apenas as tarefas necessárias. As linguagens funcionais tendem a ter um suporte maior a isso fazendo com que esse trabalho seja mais simples, mas não é algo que faça parte da linguagem em si. \ Falei linguagens, mas se pá a única que sei que faz isso é Haskell hehe :v
Marquei algumas partes desse fragmento sobre escopo léxico Por aí, mas não tem muita ligação, o escopo lexico define que um identificador (eg. nome de variavel) pertence a um escopo X e todos os ambientes ou escopos dentro dele tem acesso a ele, porém não o contrário já que é estático e é criado assim que o código é lido. Ex:
let x = 10; // escopo global
function teste() { x = x * 2; }
function teste2() { let x = 50; console.log(x); }
console.log(x) // 10 teste(); console.log(x) // 20 :: alterou o valor de x pra 20 teste2(); // 50 :: alterado o valor de x apenas dentro da função, mas não é o mesmo x anterior console.log(x) // 20 :: o valor de x continuou 20
Se ver o `x` está no escopo global, por isso posso usá-lo e alterá-lo dentro de `teste()`, e por conta do `let` eu não posso recriá-lo (usar `let x` novamente)...
Mas veja, eu uso `let x = 50` dentro de `teste2()`, e funciona, porque `x` passa a existir nos dois contextos, mas sendo valores diferentes, pois o `x` dentro da função é um e o de fora é outro por terem sidos criados em contextos diferentes, tanto que o javascript remove a referência de `x` global ao usar o `let` dentro da função `teste2()`.
Ufa! :sweat_smile:
~espero não ter falado nenhuma besteira~
Avançamos bastante na discussão. Muito obrigado! Vou reler algumas coisas com mais calma, porque foi bastante conteúdo.
Oi @Pliavi e companhia! (Dando uma atualizada na discussão) Achei esse artigo muito bom. Está em inglês, mas acho que é muito útil, especialmente para quem está na aula 32, pois fala de closure, IIFE, e de outras coisas interessantes.
Acho que consegui entender um pouco melhor minha dúvida inicial. Parece que na imagem acima, por exemplo, quando counter()
é chamada, um ambiente léxico "vazio" (sem variáveis locais) é criado para ela. Porém, toda função, quando criada, recebe uma propriedade chamada [[Environment]]
, que contém uma referência ao ambiente léxico onde foi criada. Todo ambiente léxico é composto de duas coisas: um registro do ambiente (os retângulos, na imagem), que é um objeto que contém todas as variáveis locais como suas propriedades (e também o valor de this
(achei interessante essa info), e uma referência ao ambiente externo (indicada pelas setas, na imagem), geralmente tudo que está fora do par de chaves { }
da função.
Sendo assim, o [[Environment]]
de counter
é a referência externa para a chamada counter()
. Tudo que está fora das chaves da função aninhada, retornada, em makeCounter
é acessível. counter()
encontra count
entre as variáveis de makeCounter
, podendo incrementá-la. Enquanto essa referência existir, o valor de count
poderá ser acessado e modificado no "local" (counter
). Uma variável diferente, counter2
, por exemplo, teria um [[Environment]]
totalmente independente do [[Environment]]
de counter
.
O estado inicial é preservado enquanto houver referências a ele, enquanto houver funções que o usem.
Acho que essa ideia de estado inicial privado tem a ver a organização do código do jogo da memória, certo? (Aqui estou arriscando muito no vocabulário) Parece que permite um código mais "modular", com menos repetição de código, certo? Por exemplo, toda uma estrutura complexa pode ser replicada "modularmente"(?) com uma declaração de variável que tem como valor o que é retornado de outra função. Acho que, como você falou, programação funcional parece se basear muito nessa liberdade do JS.
O que acha?
Bom dia. Alguém poderia me ajudar a entender por que registrar no console o valor de
twice
mostra a função que é retornada comfactor
, e não com o argumento2
, como emmultiplier(2)
? Entendo quando é registradotwice(5)
, em que o valor retornado é10
, mas não entendo esse primeiro caso.