JavaScript - nuances de myArray.forEach vs for loop

88

Já vi muitas perguntas que sugerem o uso de:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

ao invés de:

for (var i in myArray){ /* ... */ }

para matrizes, devido a iteração inconsistente ( veja aqui ).


No entanto, não consigo encontrar nada que pareça preferir o loop orientado a objetos:

myArray.forEach(function(item, index){ /* ... */ });

O que parece muito mais intuitivo para mim.

Para meu projeto atual, a compatibilidade com o IE8 é importante e estou pensando em usar o polyfill da Mozilla , mas não tenho 100% de certeza de como isso funcionará.

  • Há alguma diferença entre o loop for padrão (o primeiro exemplo acima) e a implementação de Array.prototype.forEach por navegadores modernos?
  • Há alguma diferença entre as implementações de navegador moderno e a implementação do Mozilla vinculada acima (com especial atenção ao IE8)?
  • O desempenho não é um problema, apenas a consistência com que as propriedades são iteradas.
Michael Lewis
fonte
6
Não é possível breaksair forEach. Mas uma grande vantagem é criar um novo escopo com a função. Com o polyfill você não deve ter problemas (pelo menos eu não encontrei nenhum).
hgoebl
Os problemas que você pode ter com o IE mais antigo não são o shim em si, mas o construtor de array quebrado / literal wrt holesonde undefineddeveria estar e outros métodos quebrados, como slicee hasOwnPropertywrt para objetos DOM do tipo arrayl. Meus testes e es5 shimdemonstraram tais métodos de shim em conformidade com a especificação (shim do MDN não testado).
Xotic750 de
1
E não é para sair de um forloop, é para isso que someserve.
Xotic750
1
"No entanto, não consigo encontrar nada que pareça preferir o loop orientado a objetos:" Prefiro chamá-lo de modo funcional ao invés de imperativo.
Memke
Você pode usar Array.find()para sair do loop após encontrar uma primeira correspondência.
Mottie

Respostas:

121

A diferença mais substantiva entre o forloop e o forEachmétodo é que, com o primeiro, você pode breaksair do loop. Você pode simular continuesimplesmente retornando da função passada para forEach, mas não há como parar completamente o loop.

Além disso, os dois realizam efetivamente a mesma funcionalidade. Outra pequena diferença envolve o escopo do índice (e todas as variáveis ​​que contêm) no loop for, devido ao içamento de variável.

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

No entanto, acho que forEaché muito mais expressivo - representa sua intenção de iterar por meio de cada elemento de um array e fornece uma referência ao elemento, não apenas ao índice. No geral, tudo se resume ao gosto pessoal, mas se você puder usar forEach, eu recomendo.


Existem algumas diferenças mais substanciais entre as duas versões, especificamente em relação ao desempenho. Na verdade, o loop for simples tem um desempenho consideravelmente melhor do que o forEachmétodo, conforme demonstrado por este teste jsperf .

Se essa performance é ou não necessária para você, cabe a você decidir e, na maioria dos casos, eu preferiria a expressividade à velocidade. Essa diferença de velocidade provavelmente se deve às pequenas diferenças semânticas entre o loop básico e o método ao operar em matrizes esparsas, conforme observado nesta resposta .

Se você não precisa do comportamento forEache / ou precisa sair do loop antes, pode usar o Lo-Dash _.eachcomo alternativa, que também funcionará em vários navegadores. Se você estiver usando jQuery, ele também fornece um similar $.each, apenas observe as diferenças nos argumentos passados ​​para a função de retorno de chamada em cada variação.

(Quanto ao forEachpolyfill, ele deve funcionar em navegadores mais antigos sem problemas, se você optar por seguir esse caminho.)

Alexis King
fonte
1
É importante notar que as versões da biblioteca eachnão se comportam da mesma maneira que a especificação ECMA5 de forEach, elas tendem a tratar todos os arrays como densos (para evitar bugs do IE, desde que você saiba). Caso contrário, pode ser um "pegadinho". Como referência github.com/es-shims/es5-shim/issues/190
Xotic750
7
Além disso, existem alguns métodos de protótipo que permitem que você interrompa, como o Array.prototype.someque fará um loop até que você retorne um valor verdadeiro ou até que tenha feito um loop completo através do array. Array.prototype.everyé semelhante a, Array.prototype.somemas para se você retornar um valor falso.
axelduch
Se você quiser ver alguns resultados de teste em navegadores diferentes e tais shims, você pode dar uma olhada aqui, ci.testling.com/Xotic750/util-x
Xotic750
Além disso, usar Array.prototype.forEach colocaria seu código à mercê da extensão. Por exemplo, se você estiver escrevendo um código para ser incorporado em uma página, há uma chance de que Array.prototype.forEach seja sobrescrito por outra coisa.
Marble Daemon
2
@MarbleDaemon A chance disso é tão minúscula que é efetivamente impossível e, portanto, insignificante. Substituir Array.prototype.forEachpor alguma versão não compatível teria o potencial de quebrar tantas bibliotecas que evitá-lo por esse motivo não ajudaria de qualquer maneira.
Alexis King
12

Você pode usar sua função foreach personalizada, que terá um desempenho muito melhor do que Array.forEach

Você deve adicionar isso uma vez ao seu código. Isso adicionará uma nova função ao Array.

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

Depois, você pode usá-lo em qualquer lugar, como Array.forEach

[1,2,3].customForEach(function(val, i){

});

A única diferença é 3 vezes mais rápido. https://jsperf.com/native-arr-foreach-vs-custom-foreach

ATUALIZAÇÃO: Na nova versão do Chrome, o desempenho de .forEach () foi melhorado. No entanto, a solução pode fornecer desempenho adicional em outros navegadores.

JSPerf

Gaivota
fonte
4

É sugerido por muitos desenvolvedores (por exemplo, Kyle Simpson) para usar .forEachpara indicar que o array terá um efeito colateral e .mappara funções puras. foros loops se encaixam bem como uma solução de propósito geral para um número conhecido de loops ou qualquer outro caso que não se encaixe, pois é mais fácil de se comunicar por causa de seu amplo suporte na maioria das linguagens de programação.

por exemplo

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));

Em termos de convenção, isso parece melhor, no entanto, a comunidade ainda não estabeleceu uma convenção sobre os for inloops de protocolo de iteração mais recentes . Geralmente, acho uma boa ideia seguir os conceitos de FP que a comunidade JS parece estar aberta a adotar.

Steviejay
fonte