Is optimizing JavaScript for loops really necessary?
I read that it is advised to optimize loops in JavaScript by not reading the length attribute of an array every iteration in the loop header.
So, we should rather do this:
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]);
}
instead of this:
var names = ['George','Ringo','Paul','John'];
for(var i=0;i开发者_StackOverflow社区<names.length;i++){
doSomeThingWith(names[i]);
}
However, I created a small testcase to compare the two techniques, but sometimes the first case was faster and sometimes the second one.
Which version would you recommend?
First, I should say that this answer is written in 2011 and these things change over time (as browser interpreters optimize more and more things) so if you really want to know the current state of the world, you have to run tests on current browsers.
Run your own jsperf test on any version of IE. There you will see a consistent difference between the two methods or many other old browsers. You apparently only ran it on Chrome which is so fast and so optimized that there is a negligible difference between the two methods. On IE9 (which is likely way better than IE7 and IE8), the method which pre-caches the length is 31% faster.
A jsperf test designed for this question gives quantitative results on this question. In questions like this one should just go to jsperf to see what the real difference is rather than so much speculation.
It shows a difference in the browsers I tried that ranges from almost no difference to a pretty sizable difference depending upon the browser. In Chrome, there's almost no difference. In IE9, storing the length first is almost 50% faster.
Now, whether this speed difference matters to your scripts depends on the specific code. If you had a huge array that you were looping through frequently, it could make a meaningful difference in some browsers to use this form:
for (var i = 0, len = list.length; i < len; i++) {
// do code here
}
In a slightly different test case when using live pseudo arrays returned by some DOM functions, there was still a difference in speed, but not as magnified (I expected the difference to be greater on DOM pseudo live arrays, but it wasn't).
In practice, I tend to use the short version (less typing) when I don't think my section of code is speed critical and/or the array is not large and I would use the longer version that pre-caches the length if I am consciously thinking about speed or the array is huge or I'm doing a lot of iterations over the same array.
There are a couple other programming reasons to pre-cache the length. If you will be adding elements to the end of the array during the loop and you don't want to the loop to iterate over those newly added elements, then you will NEED to pre-load the length and only iterate over the initially present elements.
for (var i = 0, len = list.length; i < len; i++) {
if (list[i] == "whatever") {
list.push("something");
}
}
Keep in mind that browsers are continually evolving and adding more and more optimizations so an optimization that shows great benefit in 2011 may be essentially built into a more modern browser in the future so the hand coded optimization is no longer needed. So, if you're trying to optimize something for today's performance, you have to test in today's browsers, you can't just rely on things you read that may be a few years old.
This advice was always a micro-optimization at best, and with all of the work being done on the speed of Javascript engines, it's unlikely to be a measurable difference any more. Perhaps somewhere in a very long very tight loop it might make a difference, but I doubt it.
For some reason, programmers tend to focus on speed above all else, even when it is unwarranted. Think about correctness, then readability.
I would recommend the second:
var names = ['George','Ringo','Paul','John'];
for (var i = 0; i < names.length; i++) {
doSomeThingWith(names[i]);
}
because it is more concise and more idiomatic. You won't ever need to use the first unless you are doing some absurd micro-optimization.
As a general rule, caching the "stop value" of a loop (in your case names.length) is only valuable if it is a calculated value. For the array in question, it is just a lookup so you will gain little by caching it.
Define "really necessary"
.
If you loop over an array of 4 elements, i don't think that even IE would mind, but have in mind that you maybe have to loop through some dom elements; say that you have a list (ul
) with 1.000.000 (or more) entrances (li
). I think that declaring an extra variable would save you checking the length property of that ul a milion times.
Maybe I exaggerated a bit with the million part, but take a look at the test results on only 10000 li
's.
The optimized loop was almost a hundred times faster than the "normal" one.
My conclusion: optimize your loops... it can't harm you (or your code or your browser).
I'd recommend
var names = ['George','Ringo','Paul','John'];
var length = names.length;
for(var i=0;i<length;i++){
doSomeThingWith(names[i]);
}
2017 Updated answer
You should use the optimized/best practice way.
In your exact example: it is so trivial, that it doesn’t matter. Even at 50% performance difference, as stated by @jfriend00, it doesn’t mean much. CPUs (to include current smart phones) can do millions of calculations per second. Meaning that the fraction of a millisecond just won’t even register to the user, which is in line with what @Ned Batchelder posted.
However, coding should not be about what you can get away with. That said, as @DwB said, the “…stop value…only valuable if it is a calculated value.” With that in mind, the following code gives an example of a time-wasting function to return a stop value. Here it becomes apparent just how different the speed is. Multiply the potential shortcomings across a server, complex client-side code, and other intensive calculations, and you will improve the user experience by using best practices.
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";
}
}
<h1>Loop Tester</h1>
<form onSubmit="return false;">
Loop Length <input id="loopCount" type="text" value="100,000"><br>
<br><br>
<button id="runButton" onClick="runLoop();">Run Test</button>
<div id="waitDiv" style="visibility: hidden"><img src="https://i.stack.imgur.com/5qXc3.gif"></div>
<br><br>
</form>
<div><p>Times are in milliseconds</p>
<div id="cached"></div>
<div id="inline"></div>
</div>
Please refer to this post, that talks about optimising JS loops.
So a simple solution for you problem would be:
let arr = ["a", "b", "c", "d", "e"]
let i = arr.length
while(i--) {
callFn();
}
The above code would run faster as compared to other conventional looping techniques like
for(let i = 0; i < arr.length; i++) {}
Because the above code would have to fetch arr.length
on every iteration.
精彩评论