Artigo original: The Differences Between forEach() and map() that Every Developer Should Know

O JavaScript tem alguns métodos práticos que nos ajudam a percorrer os elementos de um array. Os dois mais usados para isso são Array.prototype.map() e Array.prototype.forEach().

Eu acho os dois um pouco confusos, especialmente para um iniciante, pois ambos percorrem o array, executando uma função em cada elemento. Então, qual é a diferença?

Neste artigo, veremos os seguintes pontos de cada método:

  • Definições
  • O valor de retorno
  • Capacidade de encadear outros métodos
  • Mutabilidade
  • Velocidade de execução
  • Considerações finais

Definições

O método map recebe uma função como parâmetro (a esse processo onde a função é passada para outra função como parâmetro, atribuímos o nome de  callback – para mais detalhes, leia aqui). Em seguida, o método executa essa função em cada elemento do array, retornando um novo array preenchido com os resultados da chamada da função.

Isso significa que ele retorna um novo array contendo uma representação de cada elemento, sempre retornando o mesmo número de elementos. Veja o exemplo abaixo:


const meuArrayIncrivel = [5, 4, 3, 2, 1]

meuArrayIncrivel.map(x => x * x)

// >>>>>>>>>>>>>>>>> Saída: [25, 16, 9, 4, 1]

Assim como em map , o método forEach() também recebe uma função como argumento e é executado uma vez para cada elemento do array. Porém, em vez de retornar um novo array, como faz o método map, ele retorna o valor undefined.

const meuArrayIncrivel = [
  { id: 1, nome: "John" },
  { id: 2, nome: "Ali" },
  { id: 3, nome: "Mass" },
]

meuArrayIncrivel.forEach(elemento => console.log(elemento.nome))
// >>>>>>>>> Saída :  John
//                    Ali
//                    Mass

1. O valor de retorno

A primeira diferença entre map() e forEach() é o valor de retorno. O método forEach() retorna o valor undefined e o map() retorna um novo array com os elementos transformados. Portanto, mesmo que eles façam o mesmo trabalho, seu valor de retorno ainda será diferente. Observe a diferença no valor de retorno:

const meuArrayIncrivel = [1, 2, 3, 4, 5]
meuArrayIncrivel.forEach(x => x * x)
//>>>>>>>>>>>>>valor de retorno: undefined

meuArrayIncrivel.map(x => x * x)
//>>>>>>>>>>>>>valor de retorno: [1, 4, 9, 16, 25]

2. Capacidade de encadear outros métodos

A segunda diferença entre esses métodos de array é o fato de que map() é encadeável. Ou seja, você pode chamar outros métodos como reduce(), sort(), filter() e assim por diante depois de executar o método map() em um array.

Isso é algo que você não pode fazer com forEach() porque, como você pode deduzir, com base no valor de retorno, não é possível iterar sobre um valor undefined, visto que já não é mais um array.

const meuArrayIncrivel = [1, 2, 3, 4, 5]
meuArrayIncrivel.forEach(x => x * x).reduce((total, valor) => total + valor)
//>>>>>>>>>>>>> Uncaught TypeError: Não é possível ler a propriedade 'reduce' de undefined
meuArrayIncrivel.map(x => x * x).reduce((total, valor) => total + valor)
//>>>>>>>>>>>>>valor de retorno: 55

3. Mutabilidade

Em geral, a palavra "mutar" tem o significado de mudar, alterar, modificar ou transformar. No mundo do JavaScript, ela tem o mesmo significado.

Um objeto mutável é um objeto cujo estado pode ser modificado após ser criado. Então, o que dizer sobre forEach e map em relação à mutabilidade?

Bem, de acordo com a documentação da MDN:

O forEach() não modifica o array sobre o qual ele é chamado. Entretanto, o retorno da função chamada por ele, que é uma função de callback, pode fazer isso.

O map(), por sua vez, também não modifica o array no qual é chamado (embora a função de callback, se invocada, possa fazê-lo).

Pois é, JavaScript é estranho.

giphy

Temos duas definições muito parecidas. Sabemos que ambos os métodos recebem a função de callback como argumento. No entanto, afinal de contas, qual deles se baseia na imutabilidade?

Bem, na minha opinião, essas definições não são, de fato, claras. Para saber qual deles não muda o array original, é preciso primeiro verificar como funcionam estes dois métodos.

O método map() retorna um array totalmente novo, com o resultado da transformação realizada pela função contendo os valores dos elementos do array original, sendo que o número de elementos permanece o mesmo. Já o forEach(), mesmo retornando o valor undefined, consegue modificar o array original dentro da própria função de callback.

Portanto, fica claro que o método map() se baseia na imutabilidade, enquanto forEach() é um método mutante.

4. Velocidade de execução

Em termos de velocidade de execução, eles são muito pouco diferentes. Isso é realmente importante? Bem, depende de muitos fatores, como seu computador, quantos dados você está lidando etc.

Você pode verificar por si mesmo com este exemplo abaixo, ou utilizando o jsPerf para ver qual deles é mais rápido.

const meuArrayIncrivel = [1, 2, 3, 4, 5]

const inicioForEach = performance.now()
meuArrayIncrivel.forEach(x => (x + x) * 10000000000)
const finalForEach = performance.now()
console.log(`Velocidade do [forEach]: ${finalForEach - inicioForEach} milisegundos`)

const inicioMap = performance.now()
meuArrayIncrivel.map(x => (x + x) * 10000000000)
const finalMap = performance.now()
console.log(`Velocidade do [map]: ${finalMap - inicioMap} milisegundos`)

Considerações finais

A escolha entre map() e forEach() sempre dependerá do que você precisa em cada caso. Se você for modificar, substituir ou usar esses dados, você pode escolher o map(), pois ele retorna um novo array com os dados transformados.

Contudo, se você não precisa retornar um array, não utilize o map() - prefira usar o forEach() ou até mesmo um laço for.

Espero que este artigo esclareça a diferença entre esses dois métodos. Se você souber de outras diferenças, compartilhe-as enviando comentários.

Agradeço por sua leitura.