First Class Objects, High-Order functions, Funções Anônimas e Closures no JavaScript

Felix Costa
Blog do LFDev
Published in
10 min readApr 24, 2017

As vezes em nossa rotina de estudo nos deparamos com certos termos e conceitos que não tínhamos visto ou mesmo que não conseguimos assimilar e trazer isso pra nossa realidade de estudos. Principalmente pra quem segue o modelo autodidata, onde você não segue a risca um modelo e cronograma por vezes pulamos esses mesmos termos e conceitos desconhecidos e que são essenciais para avançarmos para o próximo nível.

Dentro do universo JavaScript não é diferente e mesmo tendo ideia de cada um dos termos que irei apresentar nesse post, já me vi os confundindo ou mesmo os esquecendo, então vamos entender de maneira direta qual a ligação de cada um deles e entender de maneira breve o que é cada um.

Devo alerta-lo que esse post não tem o objetivo de esgotar os assuntos relacionados a cada tópico discutido e sim somente abordar uma introdução simples para entender as diferenças. No final do artigo deixo várias referências para aí sim estudar e se aprofundar em cada tópico falado. Esse artigo serve mais para tirar dúvidas entre cada um dos termos citados.

Pre-Requisitos

O único pre-requisito para você ter total entendimento do texto é já estar um pouco familiarizado com a sintaxe do JavaScript (Não precisa ser o ES6+), ter uma ideia do que a Programação Funcional propõem resolver (basicamente conceito mesmo). Caso algo não seja assimilado, deixe sua dúvida nos comentários ou anote os termos desconhecidos para sua próxima pesquisa/aventura.

Funções

Funções são velhas conhecidas já e em JavaScript elas seguem a mesma estrutura que em outras linguagens. Ok, isso sabemos.

Uma função simples em JavaScript que retorna o triplo de um parâmetro qualquer

A função acima é praticamente a mesma se for aplicada em outras linguagens. Porque a ideia das funções é exatamente essa. Mas há uma coisa que nem todas as linguagens que dão suporte a funções possuem. E é a nossa primeira letrinha misteriosa.

First Class Objects (Objetos de Primeira Classe)

Em JS e em outras linguagens (principalmente as linguagens funcionais ou que dão suporte a esse paradigma), funções são valores. Na prática podemos simplesmente dizer que as funções são um tipo especial de objeto e como qualquer outro objeto nós podemos trata-lo da mesma forma como tratamos os nossos objetos em JavaScript.

Mesmo o JavaScript não sendo uma linguagem puramente Funcional, podemos tomar algumas ações que classificam as funções JavaScript como Objetos de Primeira Classe, como por exemplo, jogar uma função para uma variável.

Funções que são Objetos de Primeira Classe podem ser associados a qualquer outro tipo de objeto

Mas como isso é possível? No JavaScript funções são objetos, como mencionei acima. As funções herdam de um tipo especial chamado Object e podem ser usadas atribuídas com uma chave contendo um respectivo valor.

Da mesma forma como podemos passar uma função para uma variável, podemos usar funções dentro de nossos objetos literais e usa-los como métodos de nossos objetos.

Exemplo usado em um artigo do Ryan Christiani demonstrando como usar funções como métodos em nossos objetos

No exemplo acima, criamos dois objetos simples e cada um deles possui uma função que está atribuída a uma chave específica do objeto. E podemos executa-la/acessa-la da mesma forma como acessamos propriedades comuns.

Outra característica que torna o JavaScript como First Class Object é o fato de podemos passar funções para outras funções como argumentos e até mesmo retornar uma chamada de função dentro de uma função…. Parece confuso? Calma, vamos usar essas duas características para partimos para o nosso segundo tópico.

Higher-order Functions (Funções de Ordem Superior) ou Callbacks

Justamente pelas funções serem First-Class Object, podemos passar uma função como argumento para outra função e depois executar essa função passada ou até mesmo retorná-la para ser executada posteriormente.

Uma pequena nota é que as funções de ordem superior também são conhecidas como Callbacks no JavaScript. Então basicamente a ideia é a mesma com identificações diferentes.

As Funções de ordem superior ou Funções de Callback são derivadas do paradigma de programação funcional e ao contrário do muitos imaginam não é necessário ser um ninja do clã das sombras para entender ou aplicar esses padrões de codificação. Inclusive você mesmo já deve ter usado / visto e nem sabia que era simplesmente isso.

Passando funções como argumento

Vamos imaginar que precisamos dar um olá para uma pessoa, o nosso famoso Hello World. Então temos nossa função helloWorld. E vamos imaginar que precisamos imprimir essa mensagem em várias linguás diferentes então eu preciso que essa função imprima mensagens em inglês, em francês e etc.

Uma coisa que nossa função precisa é o nome da pessoa a quem queremos cumprimentar então podemos já afirmar que nossa função recebe um nome como argumento: helloWorld(name).

Mas como eu poderia resolver o problema das mensagens diferentes de acordo com a minha escolha sem precisar criar um helloWorldEnglish, HelloWorldPortuguese, HelloWorldFrancais…?

Nesse exemplo nós temos várias funções, cada uma com um retorno de “ola” diferente. Como chamar a função helloWorld e ter seu output de acordo com uma língua específica? Basta eu passar uma função especial para essa finalidade como argumento e dentro da nossa função principal executar a função que passamos.

Veja que helloWorld recebe dois argumentos: languageFunction e name. Nesse momento eles podem ser qualquer coisa e apesar de ter o final Function eu somente usei para deixar claro na assinatura da função que trata-se de uma função mas isso é opcional. E dentro da nossa função principal, sabendo que languageFunction será uma função e sabendo que ela tem um objeto de retornar uma string qualquer a depender da lingua, basta eu concatenar isso com o nosso segundo argumento, que é o nome da pessoa, e pronto.

A função imprime no nosso console uma mensagem de olá e essa mensagem será diferente de acordo com a função de língua que passarmos. Seja qual for, se o comportamento da função for semelhante a esse, será executado.

Esse é um exemplo pouco prático e foi usado somente para fins didáticos. Nesse momento começamos a entender que com funções de ordem superior, podemos passar funções para outras funções e não há limite para isso. Você pode chegar num nível de encadeamento grande de funções. Claro, esse grande nível traz diversos problemas, que você pode se aprofundar melhor nas referências sobre Callbacks que eu deixarei no final. Por fim, mais um exemplo:

Se você já usou ou já viu um código em jQuery então aí está: uma função de ordem superior. A função on() aceita uma função como argumento e essa função será executada.

Claro que nesse ponto você pode estar se perguntando porque isso seria útil. Um pequeno spoiler sobre porque isso é útil: composição de funções. Dessa forma podemos manter nosso código em funções que possuem um objetivo bem definido e construir funções maiores que utilizam a lógica dessas outras funções. Sem contar que se eu precisar, em algum lugar do sistema, posso usar as funções de languageFunctions isoladamente.

Claro, com esse exemplo isso não fica visível, mas a ideia é justamente manter as funções desacopladas e aí sim, em algum momento do sistema, juntar várias funções independentes que retornam um super resultado.

Retornando funções dentro de uma função

Esse ponto também pode assustar pela descrição mas não é nada demais. Com o conceito de funções de ordem superior é possível que o retorno de uma função principal seja uma função qualquer que foi passada como argumento. Vamos usar o mesmo exemplo que usamos acima. Observe a leve mudança:

A única diferença é que ao invés de executar a função languageFunction dentro da função helloWorld, nós passamos ela como retorno. O resultado disso é exatamente o mesmo. Quando chamarmos a função helloWorld, passando os argumentos necessários, será retornado pra gente o seguinte:

  1. O resultado da função que foi passada como argumento, que no caso é languageFunction
  2. A string que representa o nome do cliente.

E isso será apresentado para nós como um return só.

Então recapitulando e resumindo: High-order functions nos permitem, basicamente, passar funções para funções e retornar funções dentro de outras funções. Essas características fazem parte do conceito da programação funcional e dentro do JavaScript são amplamente usadas com objetivo de tornar nosso código mais declarativo (menos imperativo), baseado em funções (composições), mantendo uma maior abstração e com uma abordagem funcional, que tem ganhado bastante buzz ultimamente devido a sua flexibilidade e conceito.

Anonymous Functions (Funções anônimas)

Em todos os nossos exemplos acima, usamos funções que tinham um identificador. Mesmo as funções que eram associadas a variáveis possuem um identificador que é exatamente a chave a qual jogados a função. Mas em JavaScript, assim como em outras linguagens que suportam essa feature, podemos criar funções que não possuem uma identificação, um nome. Essas são as funções anônimas ou funções sem nome.

Temos a função result que recebe uma função que tem como objetivo nos apresentar o triplo de um determinado número. Se fosse com base no que vimos até agora, criaríamos uma função triple e só passaríamos ela depois, certo? Mas podemos fazer isso em tempo de execução, como mostra o código entre as linhas 7 e 9.

O JavaScript nos permite adicionar uma definição de função nesse local. Isso é frequentemente usado em algumas funções nativas ou até mesmo naquele trecho de código mais acima de jQuery, lembra?

A função on recebe uma função e podemos tanto passar ela de maneira anônima ou criar ela mais acima e então jogar seu identificador lá. O resultado é o mesmo.

No gist acima eu deixei as duas versões, anonymous e named. O resultado esperado é o mesmo e a decisão entre escolher um ou outro depende da análise e do objetivo dessa função. Se há a possibilidade de usa-la em outros contextos do seu código, então certamente é melhor ter uma identificação para a função. Mas se for algo mais trivial e que não é reaproveitado, não há problema em usar a abordagem anônima e cria-la em tempo de execução.

Importante ressaltar que no JavaScript essas funções também podem ser chamadas de funções lambda. Então, seja anonymous function ou lambda, o objetivo é somente criar uma função em tempo de execução e sem uma identificação fixa.

Closures

Esse pode complicar um pouco as coisas, mas vamos focar na objetividade e mostrar que características tornam um determinado trecho de código em JavaScript como Closure.

Uma closure é um tipo especial de objeto que combina duas coisas: a função e o ambiente onde a função foi criada.

Um closure é uma função interior que tem acesso a variáveis de uma função exterior — cadeia de escopo. O closure tem três cadeias de escopo: ele tem acesso ao seu próprio escopo (variáveis definidas entre suas chaves), ele tem acesso as variáveis da função exterior e tem acesso as variáveis globais.

Para entender essa citação, é necessário entender antes o que são as cadeias de escopo no JavaScript. Em outro momento escreverei somente sobre essas particularidades do JavaScript em relação a escopos.

Basicamente temos uma função definida dentro de outra função. A função interna utiliza e tem ACESSO aos parâmetros e variáveis da função externa.

Em algumas linguagens, o escopo de uma variável é a região onde a variável é identificada e onde o valor existe. Em muitas linguagens, a definição de um novo escopo é expressada sempre que há uma ocorrência de chaves {}. Entre essas chaves é um escopo. Nós temos variáveis globais e que são acessíveis por todo mundo e podemos ter variáveis que só são conhecidas dentro de uma determinada função e seu escopo de acesso termina quando a chave se fecha {}.

No JavaScript, essencialmente, antes da versão ES6 (EcmaScript 2015), nós só tínhamos dois tipos de escopo: global e por função. Ou seja, ou as variáveis são globais, que são as que são definidas fora de uma função específica, ou são fechadas ao escopo de uma função e ela só existe e só é acessada dentro daquele escopo. Qualquer outra função ou até mesmo globalmente não poderíamos usa-la.

Closures (podemos entender como clausura ou função enclausurada) são funções que “capturam” as variáveis que vêm de fora da função, no caso, das funções externas e mantem as variáveis vivas mesmo após a função externa ter sido invocada / retornada.

Para usar uma Closure, basta definir uma função dentro de outra função e expor a mesma. Para expor uma função, devolva-a ou passe-a para outra função. Closures são comumente usadas para manter privacidade em objetos, por exemplo, métodos privados. O JavaScript não oferece uma maneira nativa de fazer isso, mas é possível emular métodos privados usando closures.

Exemplo de como funcionam os escopos e como eles vistos por outros escopos

Então em suma, quando nos referimos a funções dentro de funções e os valores e escopos a quais cada um dos membros do nosso código tem acesso é quando estamos falando a respeito de Closures. E nos permitem trabalhar com alguns conceitos extremamente úteis em nossas aplicações, como encapsulamento de variáveis e métodos, alteração de escopo, criação de módulos ou até mesmo criar fábricas de funções (Function Factories). Sua utilização também traz alguns contras e que vão ser debatidos quando falarmos sobre escopo dentro do JavaScript e suas particularidades especiais.

Palavras finais

Espero que esse artigo seja útil para você e apesar de não se aprofundar em nenhum tópico específico espero que ele tenha cumprido com o objetivo que era acabar com as pesquisas que eu fazia do tipo: anonymous vs callback vs lambda vs whatever in javascript.

Nos próximos posts relacionados a JavaScript, pretendo abordar a respeito de escopos, e irmos evoluindo a respeito de alguns temas específicos da linguagem. Qualquer dúvida, correção ou crítica, peço-lhe que me informe.

Referências

--

--

Felix Costa
Blog do LFDev

CTO at Sotero Tech. Passionate about code, web, clean code and challenges.