lots of discussion calls for further explanation.
i throw in a little general performance tuning strategy to provide a context for what's going on here, and why it matters.
loops are slow.
thus, less work at each iteration equals faster execution.
simple and true.
also, functions that will be commonly used should be as fast as possible. you'll have plenty of opportunity to slow things down later on...
a common task like harvesting tags should be as quick and compatible with existing native interfaces.
consider the four possible outcomes of matching a className to an element:
(in order of likelihood)
1. the tag has no className
2. the tag has a className, but the search term is not contained in the element's className
3. the tag has a className, and the search term is equal to the element's className
4. the tag has a className, and the search term is contained in the element's className ( it's one of many classes represented in the attribute)
we provide the code inside the loop four opportunities to avoid the regexp and bail out early.
these opportunities coincide with the four possible outcomes.
line by line walkthrough:
Code:
while (a = allT[i++]) {
continue advancing to the next one in our stack of all tags, and assign it to "a".
Code:
if (a.className && a.className.indexOf(cn) + 1) {
this one is critical. most tags do not have classes. the first part of the if statement "if a.className" means that any element without a class will fail, and goto the end of the if block, and thus the end of the loop. right away, most tags fail. bailing early lets us avoid a slow string (.indexOf) or regexp (.test) method execution on something with no class at all.
the second part of the if statement (a.className.indexOf(cn) + 1) performs a similar role, acting as a gate keeper to the slow regexp. if no match if found, indexOf will return -1. adding 1 to that = 0, turning a no-match evaluation into a "falsey" value, and failing the if statement.
if we got to this point we know the element has a class, and that the search term at least partially matches the element's class.
Code:
if(a.className===cn){ allCN[allCN.length] = a; continue; }
most of the time there will only be one class used, and a direct compare is super fast.
therefore, it is fastest to try to get away with a direct compare.
if it works, which it usually will since most class attributes mean one class, we add the result to the array of matches, and continue the loop from the top.
this skips over the regexp if the fast direct match is successful.
if the direct match fails we know that one of two things is true:
1. we found a match substring, like "tree" in "subtree" for instance.
2. we found the proper class among more than one classes represented in one class attribute, typically in a space-separated list.
this line:
Code:
rx.test(a.className) ? (allCN[allCN.length] = a) : 0;
runs the regexp that can determine precisely the difference between a substring and a "one of many" match.
if it's "one of many", it adds the element to the array of matches and proceed with the next loop iteration.
-------
in general, there is a point of diminishing returns using ifs to avoid work.
you could get to the point that the filtering is more intensive than the workload to be avoided.
in this case, the regexp is sufficiently slow to provide ample time to trade in exchange for a couple of if statements.
in other situations, that's not the case, and it pays to runs a few tests and find the proper balance.