views:

260

answers:

9

I was reading an article: Optimizing JavaScript for Execution Speed

And there is a section that says:

Use this code:

for (var i = 0; (p = document.getElementsByTagName("P")[i]); i++)

Instead of:

nl = document.getElementsByTagName("P");

for (var i = 0; i < nl.length; i++)
{
    p = nl[i];
}

for performance reasons.

I always used the "wrong" way, according the article, but, am I wrong or is the article wrong?

A: 

It makes sense to just assign it directly to the variable. Probably quicker to write as well. I'd say that the article might have some truth to it.

Instead of having to get the length everytime, check it against i, and then assign the variable, you simply check if p was able to be set. This cleans up the code and probably is actually faster.

Chacha102
surely getting the length of an array would be faster than querying the DOM and getting an entire nodelist though, right?
nickf
Well, if you are assigning the variable on the next line, it does make sense do reduce 2 calls to one.
Chacha102
+2  A: 

Good question - I would assume not calling the function getElementsByTagName() every loop would save time. I could see this being faster - you aren't checking the length of the array, just that the value got assigned.

var p;
var ps = document.getElementsByTagName("P");

for (var i = 0; (p=ps[i]); i++) {
  //...
}

Of course this also assumes that none of the values in your array evaluate to "false". A numeric array that may contain a 0 will break this loop.

gnarf
it's the other way round! The article claims that calling document.getElementsByTagName("P")[i] is faster - quite a surprise.
djna
And isn't just the article, it is a chapter of the book: http://www.peachpit.com/store/product.aspx?isbn=0735713243
Zanoni
Yeah - Quite surprising to me...
gnarf
+1  A: 

If you look at it from a language like C#, you'd expect the second statement to be more efficient, however C# is not an interpreter language.

As the guide states: your browser is optimized to retrieved the right Nodes from live lists and does this a lot faster than retrieving them from the "cache" you define in your variable. Also you have to determine the length each iteration, with might cause a bit of performance loss as well.

Interpreter languages react differently from compiled languages, they're optimized in different ways.

Zyphrax
+1  A: 

Interesting. Other references do seem to back up the idea that NodeLists are comparatively heavyweight.

The question is ... what's the overhead? Is it enough to bother about? I'm not a fan of premature optimisation. However this is an interesting case, because it's not just the cost of iteration that's affected, there's extra overhead as the NodeList must be kept in synch with any changes to the DOM.

With no further evidence I tend to believe the article.

djna
+2  A: 

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."

--Donald Knuth


Personally i would use your way because it is more readable and easier to maintain. Then i would use a tool such as YSlow to profile the code and iron out the performance bottlenecks.

Gary Willoughby
Agree generally, but here the penalties could be quite pernicious. Benchmarking just the loop wouldn't show the full story. I think that this may be a case where a little premature optimisation actually makes sense. Either code is pretty readable, one is known (now) to be better. To use a Java analogy: StringBuffer append or String concatenation? We don't wait for GC problems before doing it right???
djna
ah i hate this "answer" of "premature optimisation". there's nothing wrong with wanting to know which method is faster and better. optimisation is ONLY premature if it hurts in some way (readability, etc). The rest of the quote goes on to say "Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified."
nickf
A: 

I typically do this (move the length test outside the for loop)

var nl = document.getElementsByTagName("p");
var nll = nl.length;
for(var i=0;i<nll;i++){
    p = nl[i];
}

or for compactness...

var nl = document.getElementsByTagName("p");
for(var i=0,nll=nl.length;i<nll;i++){
    p = nl[i];
}

which presumes that the length doesn't change during access (which in my cases doesn't)

but I'd say that running a bunch of performance tests on the articles idea would be the definitive answer.

scunliffe
A: 

No it will not be faster. Actually it is nearly totally nonsense since for every step of the for loop, you call the "getElementsByTagName" which, is a time consuming function.

The ideal loop would be as follows:

nl = document.getElementsByTagName("P");

for (var i = nl.length-1; i >= 0; i--)
{
    p = nl[i];
}

EDIT: I actually tested those two examples you have given in Firebug using console.time and as everyone have though the first one took 1ms whereas the second one took 0ms =)

BYK
A: 

The author of the article wrote:

In most cases, this is faster than caching the NodeList. In the second example, the browser doesn't need to create the node list object. It needs only to find the element at index i at that exact moment.

As always it depends. Maybe it depends on the number of elements of the NodeList. For me this approach is not safe if the number of elemets can change, this could cause and index out of bound.

ungarida
A: 

From the article you link to:

In the second example, the browser doesn't need to create the node list object. It needs only to find the element at index i at that exact moment.

This is nonsense. In the first example, the node list is created and a reference to it is held in a variable. If something happens which causes the node list to change - say you remove a paragraph - then the browser has to do some work to update the node list. However, if your code doesn't cause the list to change, this isn't an issue.

In the second example, far from not needing to create the node list, the browser has to create the node list every time through the loop, then find the element at index i. The fact that a reference to the node list is never assigned to a variable doesn't mean the list doesn't have to be created, as the author seems to think. Object creation is expensive (no matter what the author says about browsers "being optimized for this"), so this is going to be a big performance hit for many applications.

Optimisation is always dependant on the actual real-world usage your application encounters. Articles such as this shouldn't be seen as saying "Always work this way" but as being collections of techniques, any one of which might, in some specific set of circumstances, be of value. The second example is, in my opinion, less easy to follow, and that alone puts it in the realm of tricksy techniques that should only be used if there is a proven benefit in a specific case.

(Frankly, I also don't trust advice offered by a programmer who uses a variable name like "nl". If he's too lazy to use meaningful names when writing a tutorial, I'm glad I don't have to maintain his production code.)

NickFitz