Desenvolvimento

12 set, 2018

Closures, Funções Anônimas e Funções Nomeadas

Publicidade

Quando começamos a explorar recursos de programação funcional em uma linguagem (como JavaScript, por exemplo), ouvimos falar de funções anônimas, closures e outras coisas, mas parece um salto gigante entender o que são. Este pequeno artigo busca explicar o que são e porquê são usadas.

Para começar, precisamos entender o que são funções anônimas.

Imagine que numa linguagem qualquer você tem o seguinte fragmento de código:

function churrasqueira_eletrica() {
  return “Tá pegando fogo, bicho!”;
}

Como pode ser visto, esse código define a função churrasqueira_eletrica que não recebe nenhum argumento e retorna uma frase famosa. Essa função é nomeada porque nós demos a ela um nome (churrasqueira_eletrica) e você pode acessar essa função novamente usando esse nome (executando churrasqueira_eletrica();).

Como você deve imaginar, função anônima é algo quase igual:

function() {
  return “Nada a ver”;
}

A função acima é uma função anônima pelo simples motivo de você não ter dado um nome para ela. Normalmente você faz isso para utilizar essa função como parte auxiliar de outra parte do código. Se você já mexeu com map ou reduce em JavaScript ou Ruby ou qualquer linguagem do gênero, você já viu isso.

Map e reduce são um conceito de programação funcional em que você pega uma coleção (um conjunto de coisas, por exemplo uma lista) e aplica uma função sobre cada um dos itens dessa coleção, retornando o resultado disso. Por exemplo:

minha_colecao = [1, 2, 3, 4]

# Vamos elevar todos valores ao quadrado
minha_colecao.map(function(valor) { valor * valor })
# [1, 4, 9, 16]

No código acima nós temos uma lista de números e aplicamos map nela; map espera como argumento uma função a ser aplicada em cada elemento, então definimos uma função anônima que multiplica cada elemento por ele mesmo.

Agora que você já pegou tudo isso (a maior parte do artigo), vem a parte interessante.

Uma closure não é muito mais que “uma função anônima com acesso ao ambiente em que foi definida”. Com “ambiente” entenda o acesso à memória (aos nomes de variáveis e os seus valores) no momento em que essa parte do código roda. Por exemplo:

# 12:31:00
var hora_que_rodou = Time.now()
var as_hora_no_time_doctor = function() {
  console.print(“Esta função foi definida às ” + hora_que_rodou)
  console.print(“Agora são ” + Time.now())
  console.print(“O valor da variável eita é: “ + eita)
}
var eita = 123
as_hora_no_time_doctor()
# Esta função foi definida às 12:31:00
# Agora são 12:32:18
# O valor da variável eita é: undefined

Viu? A variável hora_que_rodou tinha um valor (“12:31:00”) no momento em que definimos a closure, então ela conseguiu acessar esse valor. A variável eita, no entanto, não estava definida na hora que definimos a closure, por isso seu valor não é acessável.

Então podemos usar closures para referenciarmos um certo momento no tempo para execução de código.

Um adendo importante é que usualmente closures têm acesso de leitura a snapshots do ambiente. Isto é, o valor das variáveis somente no momento em que a closure foi definida; qualquer alteração posterior não é acessável por ela. No entanto, JavaScript (e potencialmente outras linguagens) têm acesso ao próprio ambiente, o que significa que mudanças no valor das variáveis se propagam à closure. Exemplo:

var x = 1
var eita = function() {
  console.print(x)
}
x = 2
run(eita)
# 2

Restou alguma dúvida? Coloque nos comentários que será respondida o mais cedo possível!