que utilidade tem o método javascript forEach (esse mapa não pode fazer)?

90

A única diferença que vejo em map e foreach é que mapestá retornando uma matriz e forEachnão está. Porém, eu nem entendo a última linha do forEachmétodo " func.call(scope, this[i], i, this);". Por exemplo, não é " this" e " scope" referindo-se ao mesmo objeto e não é this[i]e ireferindo-se ao valor atual no loop?

Notei em outro post que alguém disse "Use forEachquando quiser fazer algo com base em cada elemento da lista. Você pode adicionar coisas à página, por exemplo. Essencialmente, é ótimo para quando você deseja" efeitos colaterais ". Não sei o que significa efeitos colaterais.

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) { 
    scope = scope || this; 
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this); 
    } 
}

Finalmente, existem outros usos reais para esses métodos em javascript (uma vez que não estamos atualizando um banco de dados) além de manipular números como este:

alert([1,2,3,4].map(function(x){ return x + 1})); //this is the only example I ever see of map in javascript.

Obrigado por qualquer resposta.

JohnMerlino
fonte
Como encontrar a definição de uma função de JavaScript nativo, como você fez com mape forEach? Tudo o que recebo do Google são especificações de uso e tutoriais.
WoodrowShigeru de
Consulte também o agnóstico de idioma. Existe uma diferença entre foreach e map?
Bergi

Respostas:

94

A diferença essencial entre mape forEachem seu exemplo é que forEachopera nos elementos da matriz original, enquanto mapretorna explicitamente uma nova matriz como resultado.

Com forEachvocê está realizando alguma ação com - e opcionalmente alterando - cada elemento na matriz original. O forEachmétodo executa a função fornecida para cada elemento, mas não retorna nada ( undefined). Por outro lado, mappercorre o array, aplica uma função a cada elemento e emite o resultado como um novo array .

O "efeito colateral" forEaché que a matriz original está sendo alterada. "Nenhum efeito colateral" mapsignifica que, no uso idiomático, os elementos originais do array não são alterados; a nova matriz é um mapeamento um para um de cada elemento na matriz original - a transformação de mapeamento sendo sua função fornecida.

O fato de não haver banco de dados envolvido não significa que você não terá que operar em estruturas de dados, o que, afinal, é uma das essências da programação em qualquer linguagem. Quanto à sua última pergunta, seu array pode conter não apenas números, mas objetos, strings, funções, etc.

Ken Redler
fonte
4
observação do futuro: esta resposta é realmente um absurdo; .mappode fazer tudo o que .forEachpode fazer (incluindo modificar elementos), ele apenas retorna uma nova lista construída a partir da função iteradora.
Travis Webb
6
Respondendo do passado: Discordo respeitosamente. .map()cria uma nova matriz e não altera a original. Você realmente poderia modificar o array que está sendo mapeado, mas isso seria no mínimo não idiomático, se não sem sentido. .forEach(), embora semelhante, aplica sua função a cada elemento, mas sempre retorna undefined. Apesar de tudo o que foi dito acima, o OP também está perguntando sobre suas próprias funções específicas adicionadas ao protótipo do array; não havia ES5 aqui no passado.
Ken Redler
4
Ainda não há nada que forEachfaça que você não possa fazer map. Você poderia simplesmente não se preocupar com o valor de retorno de mape teria um forEach. A diferença é que ele é muito ineficaz de usar mappara tarefas em que você não deseja criar uma nova matriz com base nos resultados. Então, para aqueles, você usa forEach.
toque em
2
@poke: ..e da mesma forma, você poderia fazer o que fosse necessário com um forloop simples e antigo . Não discordo que haja alguma diferença na "eficácia", mas também não acho que alguém esteja argumentando que um tem alguma magia que o outro não pode realizar de alguma forma. Embora haja, de fato, uma coisa que mapnão se pode fazer: não retornar um array. Não há valor em ter JS correspondeu às expectativas de outras línguas como ao comportamento dos favoritos funcionais como map, reduce, filter, etc. Por exemplo, você poderia implementar reduceRightusando blocos de construção semelhantes, mas por que não usar reduceRight?
Ken Redler
1
@ChinotoVokro, seu exemplo está alterando explicitamente o array original atribuindo b.val = b.val**2 dentro do objeto retornado . É exatamente isso que estamos dizendo que é realmente possível, mas confuso e não idiomático. Normalmente, você simplesmente retorna o novo valor, e não também o atribui à matriz original:a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
Ken Redler
66

A principal diferença entre os dois métodos é conceitual e estilística: você usa forEachquando quer fazer algo para ou com cada elemento de uma matriz (fazer "com" é o que a postagem que você cita quer dizer com "efeitos colaterais", eu acho) , enquanto você usa mapquando deseja copiar e transformar cada elemento de uma matriz (sem alterar o original).

Como mape forEachchamam uma função em cada item de uma matriz, e essa função é definida pelo usuário, não há quase nada que você possa fazer com um e não com o outro. É possível, embora feio, usá-lo mappara modificar uma matriz no local e / ou fazer algo com os elementos da matriz:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

mas muito mais limpo e mais óbvio quanto à sua intenção de uso forEach:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) { 
    el.val++;
    alert(el.val);
});

Especialmente se, como geralmente é o caso no mundo real, el for uma variável legível útil:

cats.forEach(function(cat) { 
    cat.meow(); // nicer than cats[x].meow()
});

Da mesma forma, você pode usar facilmente forEach para fazer uma nova matriz:

var a = [1,2,3],
    b = [];
a.forEach(function(el) { 
    b.push(el+1); 
});
// b is now [2,3,4], a is unchanged

mas é mais limpo de usar map :

var a = [1,2,3],
    b = a.map(function(el) { 
        return el+1; 
    });

Observe também que, porque map formar uma nova matriz, ela provavelmente incorre em pelo menos algum impacto de desempenho / memória quando tudo o que você precisa é iteração, especialmente para matrizes grandes - consulte http://jsperf.com/map-foreach

Quanto ao motivo de você querer usar essas funções, elas são úteis sempre que você precisa fazer manipulação de array em javascript, o que (mesmo que estejamos falando apenas de javascript em um ambiente de navegador) é muito comum, quase a qualquer momento você está acessando um array que não está escrevendo manualmente em seu código. Você pode estar lidando com uma matriz de elementos DOM na página, ou dados extraídos de uma solicitação AJAX ou dados inseridos em um formulário pelo usuário. Um exemplo comum que encontro é a extração de dados de uma API externa, onde você pode querer usar mappara transformar os dados no formato que deseja e, em seguida, usar forEachpara iterar em seu novo array a fim de exibi-los para o usuário.

Nrabinowitz
fonte
vejo pessoas usando .map()o tempo todo para modificar elementos embutidos. Fiquei com a impressão de que era o principal benefício de usar .mapover.forEach
chovy
1
@chovy, acredito que você deve escolher com base em se deseja obter uma série de resultados. .mappode modificar elementos, sim, mas criaria um array de que você não precisa. Você também deve considerar como escolher um ou outro permite que o leitor adivinhe se você tem ou não efeitos colaterais
leewz
16

A resposta votada (de Ken Redler) é enganosa.

Um efeito colateral na ciência da computação significa que uma propriedade de uma função / método altera um estado global [wiki] . Em certo sentido, isso também pode incluir a leitura de um estado global, ao invés de argumentos. Na programação imperativa ou OO, os efeitos colaterais aparecem na maioria das vezes. E provavelmente você está fazendo uso dele sem perceber.

A diferença significativa entre forEache mapé que mapaloca memória e armazena o valor de retorno, enquanto o forEachjoga fora. Veja as especificações do emca para obter mais informações.

Já o motivo pelo qual as pessoas dizem que forEaché usado quando você quer um efeito colateral é que o valor de retorno de forEaché sempre undefined. Se não tiver efeito colateral (não muda o estado global), a função está apenas desperdiçando tempo de CPU. Um compilador otimizado irá eliminar este bloco de código e substituí-lo pelo valor final (undefined ).

A propósito, deve-se observar que o JavaScript não tem restrições aos efeitos colaterais. Você ainda pode modificar a matriz original dentro map.

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]
Jack Tang
fonte
1
Esta pergunta e suas respostas e comentários são sempre divertidos de voltar a cada dois anos. A forma como o mapa é implementado em JS, em termos de alocação de memória, é outro ângulo interessante.
Ken Redler
6

Esta é uma bela pergunta com uma resposta inesperada.

O seguinte é baseado na descrição oficial deArray.prototype.map() .

Não há nada que forEach()possa fazer que map()não possa. Ou seja, map()é um superconjunto estrito de forEach().

Embora map()seja normalmente usado para criar um novo array, também pode ser usado para alterar o array atual. O exemplo a seguir ilustra isso:

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

No exemplo acima, afoi convenientemente definido de modo que a[i] === ipara i < a.length. Mesmo assim, ele demonstra o poder map()e, em particular, sua capacidade de alterar o array no qual é chamado.

Nota1:
A descrição oficial implica que map()pode até alterar o comprimento do array no qual é chamado! No entanto, não vejo (uma boa) razão para fazer isso.

Nota2:
Embora o map()mapa seja um superconjunto de forEach(), forEach()ainda deve ser usado onde se deseja a alteração de um determinado array. Isso deixa suas intenções claras.

Espero que tenha ajudado.

Sumukh Barve
fonte
8
Na verdade, há uma coisa que forEach pode fazer e que o mapa não pode fazer - não retornar um array.
Antti Haapala
1
Você também pode usar o terceiro argumento para a função de mapeamento para transformar a matriz de destino, em vez da variável com escopo:mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926
@ pdoherty926 é verdade, mas também pode a.forEach(function(x, i, arr) { ..., o que, novamente, é uma maneira conceitualmente mais correta quando há uma intenção de transformar cada item original em oposição a mapear valores de uma matriz para outra
conny
@conny: Concordo. Além disso, consulte esta resposta para uma pergunta semelhante.
Sumukh Barve
3

Você pode usar mapcomo se fosse forEach.

Mas fará mais do que o necessário.

scopepode ser um objeto arbitrário; não é necessariamente this.

Quanto a saber se existem usos reais para mape forEach, também perguntar se existem usos reais para forou whileloops.

Wombleton
fonte
Mas por que "scope" e "this" estão sendo chamados aqui: func.call (scope, this [i], i, this); O escopo não é um parâmetro igual ao objeto atual, que é "este"?
JohnMerlino
Não, pode ser igual ao objeto atual. O próprio objeto é passado como o terceiro parâmetro do array. scope = scope || thissignifica "se scopefor falso (indefinido, nulo, falso, etc), defina o escopo thise continue".
wombleton de
Você pode me vincular a um exemplo quando não for igual a isso?
JohnMerlino de
developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… tem um em "Imprimindo o conteúdo de uma matriz com um método de objeto"
wombleton
1

Embora todas as perguntas anteriores estejam corretas, eu definitivamente faria uma distinção diferente. O uso de mape forEachpode implicar intenção.

Eu gosto de usar mapquando estou simplesmente transformando os dados existentes de alguma forma (mas quero ter certeza de que os dados originais não foram alterados.

Gosto de usar forEachquando estou modificando a coleção no local.

Por exemplo,

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

Minha regra é certificar-se de que mapvocê está sempre criando algum novo objeto / valor para retornar para cada elemento da lista de fontes e retornando -o ao invés de apenas realizar alguma operação em cada elemento.

A menos que você tenha alguma necessidade real de modificar a lista existente, realmente não faz sentido modificá-la no local e se encaixa melhor em estilos de programação funcionais / imutáveis.

Maschwenk
fonte
1

Resposta TL; DR -

map sempre retorna outro array.

forEach não. Cabe a você decidir o que ele faz. Retorne um array se quiser ou faça outra coisa se não quiser.

A flexibilidade é desejável em certas situações. Se não for para o que você está lidando, use o mapa.

charsi
fonte
0

Mais uma vez, sinto-me como um necromante adicionando uma resposta a uma pergunta que foi feita há 5 anos (felizmente, há atividades bem recentes, então não sou o único a perturbar os mortos :).

Outros já postaram sobre sua principal dúvida em relação à diferença entre as funções. Mas pelo...

há algum uso real para esses métodos em javascript (já que não estamos atualizando um banco de dados) além de manipular números como este:

... é engraçado você perguntar. Hoje mesmo escrevi um pedaço de código que atribui uma série de valores de uma expressão regular a várias variáveis ​​usando mapa para transformação. Foi usado para converter uma estrutura baseada em texto muito complicada em dados visualizáveis ​​... mas para simplificar, vou oferecer um exemplo usando strings de data, porque provavelmente são mais familiares para todos (embora, se meu problema fosse realmente com datas , em vez de mapa, eu teria usado objeto-data, que teria feito o trabalho esplendidamente por conta própria).

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

Então ... embora isso fosse apenas um exemplo - e fizesse apenas uma transformação muito básica para os dados (apenas por exemplo) ... ter feito isso sem o mapa teria sido uma tarefa muito mais tediosa.

Concedido, ele é escrito em uma versão de JavaScript que não acho que muitos navegadores suportem ainda (pelo menos totalmente), mas - estamos chegando lá. Se eu precisasse rodar no navegador, acredito que transpilaria muito bem.

Markku Uttula
fonte