Some of the “for loops” I’ve used in recent demo’s and rewrites for people have raised eyebrows. Constructs and methodologies that are very powerful, but even a lot of advanced programmers either don’t know, have never even seen, or just plain won’t use for some pedantic fairy tale reason.

Right now we have a lot of know-nothings running around screaming about “teh evuls” of “for loops” just like the nutjobs pissing, moaning, and kvetching about how “if/else” and “switch/case” are “bad”. I maintain that those making these claims know nothing about programming, and certainly don’t understand the raw efficiency of for loops in general.

There are a lot of techniques that can be leveraged to make them many times more effective than things like Array.foreach, Array.map, and so forth. Much less those cases where you just know you need to do “x” number of things. Some of the bizzaro-land hoops I’ve seen folks jump through just to avoid using a “for loop” is getting outright absurd.

Because nothing says “easier’ or “better” like writing two to ten times the code needed and spreading it out over 20 separate files, to avoid one to three lines of direct code.

The following techniques can result in cleaner, faster, and simpler code.

Let’s Talk About The Parts Of a For Loop

This is pretty basic stuff most developers THINK they know, but there are implications that even many advanced dev’s never consider. All most people know is the standard loop:

for (let i = 0; i < 200; i++) {
/* do something here */
}

But let’s look at the syntax diagram:

for ([initialization]; [condition]; [expression]) [statement]

For those who don’t understand syntax diagrams, anything inside [] is optional. Yes, even the statement.

Each of these sections are as follows:

Initialization

You declare the variables used inside the loop. What most people don’t realize is you can actually declare MULTIPLE variables.

for (var i = 0, value = 0; i < 200; i++) {
value += i;
}
console.log(value);

A wonderful example case in which VAR’s lack of block level scoping can be leveraged to your advantage, avoiding a declaration outside the loop.

In theory though, you could do any single statement here. You don’t HAVE to declare the variables. Not sure where/how that would be useful, but it’s possible.

Condition

The middle part is any loose boolean result. People seem to think it is just for greater than or less than, when in fact anything that returns loose true or loose false will work.

For example:

for (
let i = 0, divs = document.getElementsByTagName("div"), div;
div = divs[i];
i++
) {
console.log(div);
}

Old school — before we had for/of — this was one of the fastest ways to iterate through nodelists. Because the div = divs[i] ASSIGNMENT returns either a Element node or undefined , it can work as our condition of whether to keep looping or not. So long as your iterable has no null/false/empty or other loose false values in the middle of it, this will go through all the values.

The only problem is that too many nose-breathers who don’t realize this will think the single equals is a typo. That’s not a problem with the code, that’s a comprehension and education issue. They then go tugging on code they don’t even understand, and sit there wondering how they broke it. This is particularly true of the folks who over-rely on what some pedantic linter tells them.

One should also remember that in JavaScript you can comma delimit any conditions, and only the last one is actually used. This can be done anyplace a condition is tested, such as in “if”, “while”. It can even be done on assignment or return!

Expression

This part is where you typically modify variables, typically your iterator. The thing is almost any operation can be performed here, as it has no bearing on the actual logic.

Some sources call this the “final expression” and to me that’s a misleading name. It’s run in every iteration so what’s so “final” about that? I think they mean it’s “final” as it’s run at the end of every loop. To me that makes it sound like it should run after all loops.

For an example of how crazy we can get with that, loop at what we do in that next part:

Statement

What it is you actually want to loop. The kicker is that this too is optional, when omitted you just place a semi-colon after the closing parenthesis.

No joke, let’s say we had an array of callback functions we wanted to run in reverse order:

for (
let i = callbacks.length, fn;
fn = callbacks[--i];
fn()
);

That semi-colon being equivalent to saying {} there. We have omitted the statement! First time I saw that type of construct my brain exploded. That you can declare the reference value in the initialization, assign it as the condition, and execute it as the expression, with no statement block? Funky. Even stranger is that we call the function as our expression, not a statement! This is functionally identical to:

for (let i = callbacks.length - 1; i >= 0;  i--) {
callbacks[i]();
}

Putting It All Together

We can truly do some interesting things. Take for example when you want to loop a certain number of times. If we use a decrement as our condition:

for (let i = 100; i--; ) { console.log(i); }

That will loop 100 times. The decrement of “i” being our CONDITION, thus requiring no comparison or expression. This construct is interesting as it’s probably the closest you’ll get to assembly language. In fact when you do it in some C compilers it optimizes down to:

mov ecx, 100
:label
; do something here
loop :label

The most efficient looping construct in x86 / x64 machine language. ECX being the “counter” register that the LOOP command decrements, jumping back to our label if the result is non-zero, dropping through on zero.

That most high level languages don’t even provide a direct equivalent is a bit messed up. Though technically the JS (and other C syntax) equivalent would be:

let i = 100;
do {
// do something here
} while (--i);

Which is kind of sad is one of the least efficient looping constructs in JavaScript. 100% hurr durrz behind the scenes on optimization.

How about looping a function until it returns loose false?

for ( ; myFunction() ; );

Is functionally identical to:

while (myFunction()) {}
// or even
while (myFunction());

Did you know placing a semi-colon at the close of a conditional works on “while” as well? I have to admit I only learned that detail a few years back when looking at what the Google Closure Compiler was outputting for minified JS.

How about some DOM walking?

Let’s say you wanted to walk all direct child LI of #mainMenu in the page adding a space and asterisk to the end of the textContent.

I used to do it this way:

let li = document.getElementById("mainMenu").firstElementChild;
if (li) do {
li.textContent += " *";
} while (li = li.nextElementSibling);

But these days?

for (
let li = document.getElementById("mainMenu").firstElementChild;
li;
li = li.nextElementSibling;
) li.textContent += " *";

In Soviet Russia, the DOM walks you.

Meaning I no longer have the “li” variable in the parent scope. What makes that so cool is that by walking the DOM, both of these are many times faster than:

for (
let i = 0, items = document.querySelectorAll("#mainMenu > li"), li;
li = items[i];
i++;
) li.textContent += " *";

It’s even faster than:

for (let li of document.querySelectorAll("#mainMenu > li")) {
li.textContent += " *";
}

Because the DOM references are faster than JavaScript iterables. It’s sure as shine-ola better than:

document.querySelectorAll("#mainMenu > li")).forEach(
(li) => { li.textContent += " *"; }
);

Since we’re not getting the overhead of function calls involved. Sometimes — SOMETIMES — more code is actually better in terms of what you write, since under the hood it’s doing less. Assembly language programmers have known this for ages. See 8088 / 8086 where a MUL (multiply) is often slower than adding together a bunch of shifts. One line of code and 4 bytes that could take 200 clocks, vs. two shifts an assignment, and addition that ends up 20 bytes, but executes in 31 clocks. Or when you have a limited range of values and an array lookup reduces you to 4 bytes of code, and executes in a lightning fast 8 to 12 clocks. You just have to waste the memory for a lookup table.

Yes, I still program 8088 assembly for fun.

Converting a DOM walk into a for loop is particular fun since our “condition” is just “li”. Is it loose true, or loose false. This is just part of why I think the people trying to shoe-horn more advanced typecasting into JavaScript are missing out on a lot of its advantages! As I’ve said a lot of times lately:

Maybe if instead of fighting how different JavaScript is from other languages, we start embracing those differences, 90% of the code bloat nonsense we see out there and long convoluted build processes could be kicked to the curb!

But in a world riddled with paranoid xenophobic bigots, embracing our differences might as well be a mortal sin. Hardly surprising societal bigotry and fear-mongering ends up trickling down into every aspect and task.

NOT that I’ve ever been guilty of this myself. A little over two decades ago when I started playing with JavaScript I had just come off almost a decade of programming Ada. I had a rabid dislike for C syntax even though I had spent the twenty years prior working with that too. The cryptic syntax, willy-nilly lack of typecasting, bizzare incomplete object model, and so forth set my teeth on edge. For the first three or four years I made similar mistakes to what people today do. I blindly copied “gee ain’t that need” garbage off of DynamicDrive. I dove for JavaScript to do things that didn’t even need to be scripted. I even wasted time making libraries to try and make JavaScript behave more like Ada.

So much time wasted. Was three of four years before I stopped that nonsense and took the time to explore WHY JavaScript did things a certain way, and EXPLOIT those differences to my advantage. It was a very important lesson learned. Which happened about the same time I embraced semantic markup and separation of concerns. Coincidence? I think not…

Queue The Pedants

A great deal of what I just showed you will get a lot of people’s knickers in a twist, as it violates ALLEGED “good practices” spewed by people not qualified to say what a good practice is. Probably the biggest audience for this are the fools who take what “linters” say as if it were the freaking gospel.

To be brutally frank a lot of the nonsense that’s been touted as “don’t do that” are nothing more than the pet peeves of a handful of dumbasses. From objections on leveraging drop-through on CASE, to the idiotic claims of how “evil” Yodish expressions are, to as we did multiple times here using the result of assignment or operation as a condition. What separates these claims different from other “good” or “bad’ practices, is they are utterly devoid of factual supporting arguments.

When pressed to explain, the most those saying this stuff can come up with is “It’s too hard for beginners to understand”, or worse “normals are too stupid to follow that”. How insulting is that? I find that idea — that people are too stupid to learn new things — far more insulting than any diatribe of eloquent profanity anyone could possibly craft. Maybe I’ve got unrealistic naive expectations of my fellow man, but I don’t believe the majority of people are too stupid to do this stuff. The problem is too many believe it when told “that’s too advanced for you”.

EVERYTHING is hard for a beginner. Shielding beginners from advanced techniques does nothing but hobble their learning.

But truth is, it’s all projection. When it comes to these types of code structures that make it into “linters” what they’re really saying is “I’m too stupid to learn this, so nobody else should be allowed to use it!”. To blazes with that and that type of thinking.

Thus if the difference between =, ==, and === is too hard for you, and something like this:

for (i = 0, item; item = myArray[i]; i++) {

“offends” your sensibilities, or makes you think there’s a missing equals sign, please, checheckitycheckyoself, qwitcherbellyakin, and take the time to LEARN how things work.

You can tell the folks raging against “for” right now in their responses. Not that long ago I was commenting on how “for…of” was superior to Array.foreach, and someone replied something along the lines of:

You’re just stuck in the past. This is where the industry is headed, get used to change.

What made that so blasted funny? “for…of” is newer than Array.foreach.

COMEDY!

But sure, I’m the one stuck in the past.

Conclusion

Don’t let people scare you away from completely valid constructs that have driven programming from the start. Don’t let propaganda, lies, or fearmongering make you think you can’t do something or shouldn’t use something.

Loops are one of the most basic tasks needed to build a program. In JavaScript the “for” loop construct is many times more powerful than most people even realize, with a lot of performance and efficiency being left on the table. Don’t let anyone tell you otherwise. The people currently raging against basic language constructs like “for”, “while”, “if/else”, are generally just ignorant of how to use any of that stuff properly. They are diving for alternatives rather than — as I said earlier — embracing what is, before they understand enough to realize what they’re doing is in fact harder, bloated, and not worth the time of day.

EMBRACE the for loop. It’s capable of a lot more than you’ve been led to believe.

Original Source

Author

Accessibility and Efficiency Consultant, Web Developer, Musician, and just general pain in the arse