Menu

Higher-Order Functions em JavaScript: map, filter e reduce

Entenda o que são higher-order functions em JavaScript: como passar funções como argumento, retornar funções de outras funções e usar map, filter e reduce no dia a dia.

Funções são valores

Em JavaScript, uma função é um valor como qualquer outro. Dá pra guardar numa variável, colocar dentro de um array, passar como argumento para outra função ou retornar de uma. Esse detalhe, por si só, já abre as portas para um estilo inteiro de programação:

index.js
Output
Click Run to see the output here.

Depois que você se acostuma a tratar funções como valores, as funções de alta ordem deixam de parecer bicho de sete cabeças. Uma função de alta ordem (ou higher-order function) é simplesmente uma função que recebe outra função como argumento, retorna uma função, ou faz as duas coisas. É só isso.

Passando uma função como argumento

A forma mais comum: uma função que recebe um callback e cuida de executá-lo pra você. Na prática, você já usa isso o tempo todo sem nem perceber.

index.js
Output
Click Run to see the output here.

forEach é uma função de alta ordem — ela recebe a sua função e chama uma vez para cada item. O setTimeout também é de alta ordem — ele recebe a sua função e executa depois de um certo tempo. Você se preocupa com o que fazer; eles cuidam de quando e quantas vezes.

Criar a sua própria função assim é igualzinho. Veja uma função bem simples que executa um callback só quando uma condição é verdadeira:

index.js
Output
Click Run to see the output here.

action é um parâmetro que por acaso guarda uma função. Ao chamá-lo com action(), você executa o que foi passado.

As três que você vai usar de verdade: map, filter e reduce

Arrays já vêm com métodos de alta ordem que substituem a maior parte dos for que você escreveria na mão. Aprenda esses três e boa parte do seu código do dia a dia fica mais curto e mais claro.

map — transforma cada item

index.js
Output
Click Run to see the output here.

map chama sua função uma vez para cada item e junta os valores retornados em um novo array. Mesmo tamanho, conteúdo transformado. O array original fica intacto.

filter — mantém só os que passam no teste

index.js
Output
Click Run to see the output here.

filter mantém os itens em que o callback retorna um valor truthy e descarta o resto. O retorno é um novo array, que pode ser menor que o original.

reduce — condensando o array em um único valor

index.js
Output
Click Run to see the output here.

reduce é o mais versátil do grupo. O callback recebe o acumulador atual e o item da vez; o que você retornar vira o próximo acumulador. O segundo argumento (aqui o 0) é o valor inicial.

Dá pra encadear os três. É aqui que o estilo funcional realmente brilha:

index.js
Output
Click Run to see the output here.

Leitura de cima pra baixo: filtra os pedidos pagos, pega o preço e soma tudo. Sem loop, sem contador mutável, sem risco de errar índice.

Retornando uma função de outra função

A outra metade das funções de alta ordem. Uma função que monta e devolve outra função:

index.js
Output
Click Run to see the output here.

multiplyBy(2) executa uma vez e devolve uma função totalmente nova. Essa nova função ainda lembra do factor — isso é uma closure, que vai ganhar sua própria página mais à frente. Por enquanto, o que importa é o seguinte: chamar multiplyBy com argumentos diferentes te dá funções especializadas diferentes, todas construídas a partir do mesmo molde.

Esse padrão aparece em todo lugar:

index.js
Output
Click Run to see the output here.

Uma definição, duas funções reutilizáveis. Bem melhor do que escrever warn e info na mão e ter que manter os dois sincronizados.

Funções nomeadas vs callbacks inline

Dá pra passar uma arrow function inline ou passar uma função pelo nome. Os dois jeitos funcionam — escolha o que ficar mais legível:

index.js
Output
Click Run to see the output here.

Quando você passa isEven (sem os parênteses), está entregando a própria função. Se colocar (), ela é executada na hora e você acaba passando o resultado — um erro clássico de quem está começando:

nums.filter(isEven);     // correto: passa a função
nums.filter(isEven());   // errado: chama isEven sem argumentos e passa o resultado

Se o callback começar a se estender além de duas ou três linhas, extraia ele e dê um nome. O código ao redor quase sempre fica mais legível assim.

Um exemplo na prática

As funções de alta ordem em JavaScript brilham quando a gente compõe peças pequenas. Imagine que você tem uma lista de produtos e quer os nomes dos itens baratos, em estoque, tudo em caixa alta:

index.js
Output
Click Run to see the output here.

Cada helper tem uma responsabilidade só. Cada método de array faz uma única transformação. A pipeline acaba lendo como uma especificação do que você quer, e não como uma descrição de como iterar.

Quando não usar funções de alta ordem

Os métodos de alta ordem são ótimos — mas não servem como substituto de loop em toda situação:

  • Se você precisa parar no meio do caminho, um for ou for...of com break é mais claro do que tentar abortar um forEach.
  • Se o callback faz algo assíncrono, map e forEach não vão esperar a Promise. Nesses casos, use for...of com await, ou então Promise.all junto com map.
  • Se o callback está mutando estado compartilhado, você já está indo contra os pontos fortes desse estilo. Melhor voltar para um loop comum ou refatorar para retornar novos valores.

Aplicados onde fazem sentido, map, filter e reduce eliminam boa parte do boilerplate de loop do dia a dia. Usados em tudo, começam a atrapalhar a leitura. Escolha a ferramenta que deixa a intenção mais evidente.

A seguir: Objetos

Funções não são os únicos valores que valem a pena explorar. Os objetos são o carro-chefe do JavaScript para agrupar dados e comportamento relacionados — e são justamente o que preenchia a maioria dos arrays que você acabou de filtrar e mapear. É o assunto da próxima página.

Perguntas frequentes

O que é uma higher-order function em JavaScript?

É toda função que faz pelo menos uma destas duas coisas: recebe outra função como argumento ou devolve uma função como resultado. Array.prototype.map, setTimeout e addEventListener são exemplos clássicos — todas aceitam um callback e executam ele pra você no momento certo.

Qual a diferença entre map, filter e reduce?

O map transforma cada item e devolve um novo array do mesmo tamanho. O filter mantém só os itens em que o callback retorna um valor truthy, então o array resultante pode ser menor. Já o reduce combina os itens um a um até virar um único valor. As três são higher-order functions porque recebem um callback.

Por que retornar uma função de dentro de outra função?

Para criar helpers pequenos e configuráveis sem repetir lógica. Uma função multiplyBy(n) devolve uma nova função que multiplica por n — ou seja, multiplyBy(2) e multiplyBy(10) geram duas funções especializadas a partir de uma única definição. Esse padrão usa closures e aparece muito em event handlers, middlewares e bibliotecas utilitárias.

Aprenda a programar com o Coddy

COMEÇAR