A otimização do JavaScript for loops é realmente necessária?

Eu li que é aconselhável otimizar loops em JavaScript, não lendo o atributo length de uma matriz a cada iteração no header do loop .

Então, devemos preferir fazer isso:

var names = ['George','Ringo','Paul','John']; for(var i=0,j=names.length;i<j;i++){// Read array length once and assign it to a variable doSomeThingWith(names[i]); } 

em vez disso:

 var names = ['George','Ringo','Paul','John']; for(var i=0;i<names.length;i++){ doSomeThingWith(names[i]); } 

No entanto, criei um pequeno testcase para comparar as duas técnicas, mas às vezes o primeiro caso foi mais rápido e, às vezes, o segundo.

Qual versão você recomendaria?

Primeiro, devo dizer que esta resposta está escrita em 2011 e estas coisas mudam com o tempo (como intérpretes de navegador otimizam mais e mais coisas) então se você realmente quer saber o estado atual do mundo, você tem que executar testes nos navegadores atuais .

Execute seu próprio teste jsperf em qualquer versão do IE. Lá você verá uma diferença consistente entre os dois methods ou muitos outros navegadores antigos. Aparentemente, você só o executou no Chrome, que é tão rápido e otimizado que existe uma diferença insignificante entre os dois methods. No IE9 (que provavelmente é muito melhor que o IE7 e o IE8), o método que pré-armazena em cache o comprimento é 31% mais rápido.

Um teste jsperf projetado para essa pergunta fornece resultados quantitativos sobre essa questão. Em questões como essa, basta ir ao jsperf para ver qual é a verdadeira diferença, em vez de tanta especulação.

Ele mostra uma diferença nos navegadores que eu tentei, que varia de quase nenhuma diferença a uma diferença bastante considerável, dependendo do navegador. No Chrome, quase não há diferença. No IE9, armazenar o comprimento primeiro é quase 50% mais rápido.

Agora, se essa diferença de velocidade é importante para seus scripts, depende do código específico. Se você tivesse uma matriz enorme que estivesse circulando com freqüência, poderia fazer uma diferença significativa em alguns navegadores para usar este formulário:

 for (var i = 0, len = list.length; i < len; i++) { // do code here } 

Em um caso de teste ligeiramente diferente ao usar pseudoarray ao vivo retornado por algumas funções do DOM, ainda havia uma diferença na velocidade, mas não como aumentada (esperava-se que a diferença fosse maior em pseudo-arrays DOM, mas não era).

Na prática, eu costumo usar a versão curta (menos digitação) quando eu não acho que minha seção de código é crítica de velocidade e / ou o array não é grande e eu usaria a versão mais longa que pré-armazena o tamanho se eu Estou pensando conscientemente sobre velocidade ou o array é enorme ou estou fazendo muitas iterações sobre o mesmo array.

Há algumas outras razões de programação para pré-armazenar em cache o tamanho. Se você for adicionar elementos ao final da matriz durante o loop e não quiser que o loop faça uma iteração sobre esses elementos recém-adicionados, será necessário pré-carregar o comprimento e apenas iterar os elementos presentes inicialmente. .

 for (var i = 0, len = list.length; i < len; i++) { if (list[i] == "whatever") { list.push("something"); } } 

Lembre-se de que os navegadores estão evoluindo continuamente e adicionando mais e mais otimizações para que uma otimização que mostre um grande benefício em 2011 possa ser essencialmente incorporada em um navegador mais moderno no futuro, para que a otimização codificada manualmente não seja mais necessária. Então, se você está tentando otimizar alguma coisa para o desempenho de hoje, você tem que testar nos navegadores de hoje, você não pode confiar apenas em coisas que você lê que podem ter alguns anos de idade.

Esse conselho sempre foi uma otimização de micro, na melhor das hipóteses, e com todo o trabalho sendo feito na velocidade dos mecanismos de Javascript, é improvável que seja uma diferença mensurável mais. Talvez em algum lugar em um loop muito longo e muito apertado possa fazer a diferença, mas duvido.

Por alguma razão, os programadores tendem a se concentrar na velocidade acima de tudo, mesmo quando não é garantido. Pense em correção e legibilidade.

Eu recomendaria o segundo:

 var names = ['George','Ringo','Paul','John']; for (var i = 0; i < names.length; i++) { doSomeThingWith(names[i]); } 

porque é mais conciso e mais idiomático. Você nunca precisará usar o primeiro, a menos que esteja fazendo uma micro-otimização absurda.

Como regra geral, armazenar em cache o “valor de parada” de um loop (no seu caso, names.length) só é valioso se for um valor calculado. Para a matriz em questão, é apenas uma pesquisa, portanto, você ganhará pouco armazenando-a em cache.

Defina "really necessary" .
Se você percorrer uma matriz de 4 elementos, não acho que nem mesmo o IE se importaria, mas tenha em mente que você talvez tenha que percorrer alguns elementos do dom; diga que você tem uma lista ( ul ) com 1.000.000 (ou mais) inputs ( li ). Eu acho que declarar uma variável extra salvaria você verificando a propriedade length desse ul um milhão de vezes.
Talvez eu tenha exagerado um pouco com o milhão de partes, mas dê uma olhada nos resultados do teste em apenas 10000 li ‘s.
O loop otimizado foi quase cem vezes mais rápido que o “normal”.

Minha conclusão: otimizar seus loops … não pode prejudicá-lo (ou seu código ou seu navegador).

Eu recomendo

 var names = ['George','Ringo','Paul','John']; var length = names.length; for(var i=0;i 

Resposta atualizada de 2017

Você deve usar a maneira otimizada / melhor prática.

Em seu exemplo exato: é tão trivial, que não importa. Mesmo com 50% de diferença de desempenho, como dito por @ jfriend00, isso não significa muito. CPUs (para include telefones inteligentes atuais) podem fazer milhões de cálculos por segundo. Isso significa que a fração de um milésimo de segundo não será registrada para o usuário, o que está de acordo com o que @Ned Batchelder postou.

No entanto, codificação não deve ser sobre o que você pode se safar. Dito isto, como disse @DwB, o “… valor de parada… só é valioso se for um valor calculado”. Com isso em mente, o código a seguir dá um exemplo de uma function de perda de tempo para retornar um valor de parada. Aqui fica evidente quão diferente é a velocidade. Multiplique as possíveis deficiências em um servidor, o código complexo do lado do cliente e outros cálculos intensivos, e você aprimorará a experiência do usuário usando as práticas recomendadas.

  var eCount = document.getElementById("loopCount"); var waitDiv = document.getElementById("waitDiv"); var runButton = document.getElementById("runButton"); var interCount = eCount.value.replace(/\D/g,''); var names = ['George','Ringo','Paul','John']; eCount.addEventListener("input", function(){ var value = parseInt(this.value.replace(/\D/g,'')).toLocaleString(); this.value = value.toLocaleString(); }); function runLoop(){ interCount = eCount.value.replace(/\D/g,''); waitImg(true); setTimeout(function(){ var cachedTime = loopTest("cached"); var inlineTime = loopTest("inline"); document.getElementById( "cached" ).innerText = cachedTime+" Cached"; document.getElementById( "inline" ).innerText = inlineTime+" Not Cached"; waitImg(false); }, 100); // delay to allow update of DOM with waitimg gif } function loopTest(meth){ var worthlessVariable = 0; var t0 = performance.now(); if( meth == "cached" ){ for( var i = 0, len = busyCalulations(); i < len; i++) { worthlessVariable = i; } }else{ for( var i = 0; i < busyCalulations(); i++) { worthlessVariable = i; } } var t1 = performance.now(); return (t1 - t0); } function busyCalulations(){ // garbage math to simulate doing something // it returns interCount after some pointless math var limit = Math.floor(Math.random() * 20) + 20; return interCount*(limit*names.length)/(limit*names.length); } function waitImg(txt){ // display wait timer if (txt === true){ waitDiv.style.visibility = "visible"; runButton.style.visibility = "hidden"; }else{ waitDiv.style.visibility = "hidden"; runButton.style.visibility = "visible"; } } 
  

Loop Tester

Loop Length




Times are in milliseconds

Por favor, consulte este post, que fala sobre a otimização de loops JS.

Então, uma solução simples para o seu problema seria:

 let arr = ["a", "b", "c", "d", "e"] let i = arr.length while(i--) { callFn(); } 

O código acima seria executado mais rápido em comparação com outras técnicas de loop convencionais, como

 for(let i = 0; i < arr.length; i++) {} 

Porque o código acima teria que buscar arr.length em cada iteração.