Why I favor while loops
I remember that my first contact with programming loops was through the for
statement and I used it almost exclusively for many years. I’ll explain why I’ve been favoring while
loops over for
loops in the past couple years. Beware that this is only my personal preference and the reasoning behind it are very subjective. Use what works better for you.
TL;DR: while
is less verbose and more clear in many cases.
Less tokens per line
Lets take a very basic example into consideration:
for(var i = 0; i < 100; i++) { console.log(i); }
Converted into a while
loop:
var i = -1; while(++i < 100) { console.log(i); }
What bothers me more on the for
statement is that you have too many things going on the same line. Variable declaration, conditional logic and another expression to increment the variable. I find the line while(++i < 100)
way easier to read/understand than for(var i = 0; i < 100; i++)
, less tokens to grok and a single responsibility.
Another benefit that I see by moving the var
declaration to outside the loop logic is that it makes it clear that variables aren’t bound to the loop scope (JavaScript only have block scope if you use the let
keyword which isn’t standard). If you are enforcing manual variable hoisting and/or single var
statement on your code guidelines the while
loop will make even more sense.
Many operations can be executed in reverse order
Many types of operations can be executed in reverse order without affecting the end result. while
loops are very succinct and clear when looping backwards:
var n = 100; while (n--) { console.log(n); }
Compared to a for
loop that does the same:
for(var n = 99; n >= 0; n--) { console.log(n); }
Notice that on the for
loop you need to subtract 1
from the length
to achieve same result (array indexes starts at zero).
PS: Some operations are slightly faster when executed in reverse since they avoid changing the size of the Array during the operations. (change it only once upfront)
Processing Array elements
It is very common to use for
/while
loops to process array items. It’s a common knowledge that we should avoid property lookups whenever possible to increase performance, so it’s a very common practice to cache the Array.length
. By doing that the for
declaration gets even more complex:
for(var i = 0, n = arr.length; i < n; i++) { console.log(arr[i]); }
I would rather write it like this:
var i = -1, n = arr.length; while (++i < n) { console.log(arr[i]); }
Each line takes care of a single responsibility, less lines of code isn’t always a good thing.
If you are 100% sure that the array won’t contain falsy values (null
, undefined
, 0
, false
, ""
) you could make it even simpler:
/* jshint boss:true */ var cur, i = 0; while (cur = arr[i++]) { console.log(cur); }
Beware that falsy values will break the loop, use with care.
PS: I’ve been favoring Array#forEach
, Array#map
, Array#filter
in many cases since I think it focus the program code on the actual problem and not the loop logic, but I still use plain loops in some cases to iterate over arrays.
RegExp.exec
The same pattern above could be used for methods like RegExp#exec
:
var re = /\w+/g, match; while (match = re.exec('== Lorem ipsum dolor sit amet ==')) { console.log(match); }
This will execute the RegExp#exec
method over the whole string until it can’t find anything that matches the pattern. It can simplify parsing logic a lot.
Getting “clever”
I’ve seen lots of people abusing the fact that you can omit the expressions on the for
loop or that you can use the comma operator to do tricky things, but why not use the while
statement instead?
// first and last expressions are optional var i = 0, cur; for( ; cur = arr[i++]; ) { // ... }
By (ab)using the comma operator you can basically remove all the work from the while
body and move it to the expression block.
// will execute until `cur` is falsy and/or `cur.doSomething()` retuns // a falsy value var i = 0, cur; while( cur = arr[i++], cur && cur.doSomething() );
You can find a real working example of this concept applied to a very small snippet on the IE version detection script by padolsey and friends. I also use something similar on js-signals to execute all bindings of a signal until one of them stops the propagation (without the comma operator).
I’m not a fan of very clever tricks since I think they usually makes code harder to understand, but it is very good to know when/how to use these techniques since they can open your mind to simpler solutions in some cases.
Bonus: do-while
do-while
is one of these features that I rarely need, but in some cases it can be useful. On a recent project I needed a greedy algorithm to search for the next empty cell on a grid that wasn’t adjacent to a similar item (couldn’t place product bottles next to each other before user starts filtering the grid items), the easiest way to solve it was to write a do-while
:
do { // get next empty cell that can fit item cellIdx = this._getFitIndex(item, rowIdx, cellIdx); // check if cell isn't adjacent to a similar item (on current & prev rows) allowPlacement = this._checkPlacement(item, rowIdx, cellIdx); } while (!allowPlacement && cellIdx !== -1);
After the loop I just had to check if allowPlacement && cellIdx !== -1
and place the item at that index. If false
I created a new row on the grid and repeated the process.
On js-signals I use a do-while
to implement a very simple sorted insert.
The do-while
is nothing more than a sugared version of a while(true)
which means I could totally live without it as well.
// same as previous example while(true){ cellIdx = this._getFitIndex(item, rowIdx, cellIdx); allowPlacement = this._checkPlacement(item, rowIdx, cellIdx); if ((allowPlacement && cellIdx !== -1) || cellIdx === -1) break; }
top-level iterators
I’ve been using mout on almost all my projects for the past year and it is really great to access the array
and object
iterators as top-level functions:
var forEach = require('mout/array/forEach'); forEach(arr, function(item, i){ console.log(i, item); });
It would be nice if JavaScript had some kind of sugar for this kind of loops (since they are so common), maybe something that doesn’t need all the function
boilerplate and that works over array and objects (anything that is iterable), I would probably suggest a syntax like this:
// sugar for Array#forEach and Object#forOwn each(arr as [item, i]) { console.log(i, item); }
On Harmony there is a proposal for the for of
iterator, which is very similar:
// unsure if on Array we can also get the index for(item of arr) { console.log(item); }
If I was designing a language I would definitely include a feature like that, loops should have the least amount of boilerplate/distraction/landmines as possible.
Conclusion
I will probably avoid for
loops on my own projects for the foreseeable future. while
loops and ES5-like Array methods FTW (map, reduce, forEach, …)