Loops are real menaces! The vast majority of browser crashes are caused by badly constructed JavaScript loops. – I don’t have any numbers to back this up but I think it seems like a pretty reasonable statement. Even if loops aren’t the number #1 cause they’re probably still pretty near the top.
You can see how much is at stake here; constructing a loop incorrectly can leave a browser fighting for breath, so it’s a good idea to learn the basics before you go trotting off in the wrong direction.
The WHILE statement
This is probably my favourite statement in JavaScript, and it’s probably the most useful one you’ll ever come across.
It’s pretty simple too: as long as the passed condition evaluates to true the statement will continue to execute over and over again, it will only stop when the condition evaluates to false. Here’s a pretty cool example: say we want to remove all child nodes of the ‘body’ element; you might think building up a collection of elements and then removing each one is the best technique but there is an even simpler approach:
var body = document.body; while (body.firstChild) { body.removeChild(body.firstChild); } |
The statement will be executed as long as the body node has a ‘firstChild’ – when there are no child nodes left the condition will no longer be met and so the statement will not be executed. Note that you don’t have to include the curly braces to surround a single-line statement; I just think it looks nicer…
Traversing through a regular array can also be achieved using the WHILE loop. There are two well-known methods, one iterating in order, and one going backwards through the array:
// #1 : regular var myArray = ['apple','orange','lemon'], length = myArray.length, counter = 0; while (counter < length) { alert ( myArray[counter] ); counter++; } // alerts 'apple', then 'orange', then 'lemon' // #2 : in reverse var myArray = ['apple','orange','lemon'], length = myArray.length; while (length--) { alert ( myArray[length] ); } // alerts 'lemon', then 'orange', then 'apple' |
The first type is quite simple, the ‘counter’ variable increments by one every time the statement is run. So when the counter is no longer smaller than the array’s length (3) the statement will STOP running.
The second type doesn’t require a ‘counter’; it just decrements (by one) the value of ‘length’ every time the statement runs, and eventually it will be down to 0 (zero) which is a false-evaluating value and will therefore stop the condition from being met.
Performance:
It’s been tested: looping through a set of DOM elements or any array for that matter, using a reverse while loop is faster than any of the other well-known alternatives (e.g. a standard FOR loop).
One consideration you must also take is that of caching. Always cache the length of the array if you’re using it to construct a loop. And, if you need to access a property of an object within a loop you should cache that to. By ‘caching’ I just mean assigning a value to a variable:
var bigObject = { smallProp : [1,2,3,4,5,6,7] }; // BAD: var counter = 0; while ( counter < bigObject.smallProp.length) { doSomethingWith ( bigObject.smallProp[counter] ); } // GOOD: (with caching) var arr = bigObject.smallProp, length = arr.length, counter = 0; while ( counter < length ) { soSomethingWith ( arr[counter] ); } // It's a common naming convention to have the 'counter' // variable named as 'i' which I think stands for 'iterator'. // The name used doesn't really matter though, as long as // it's clear to you... |
The FOR Statement
The FOR statement is the most commonly used method for looping through arrays. I think people see it as a nicer alternative to the WHILE statement because it seems more self-contained, have a look:
var myArray = [1,2,3,4,5,6]; for ( var i = 0, arrLength = myArray.length; i < arrLength; i++ ) { // Notice that we've named the counter 'i' alert ( myArray[i] ); } |
Its format is as follows:
for ([initialExpression]; [condition]; [incrementExpression]) { // The statement (i.e. what will happen on each iteration) } |
initialExpression
: This will be run before the loop proceeds. It’s normally used to establish variables to be used within the statement. It might be considered as optional since leaving the space blank (but retaining the semi-colon) doesn’t break anything. Please note that when you create new variables within this expression they are created within the current scope, no new scope is created.condition
: Just like in the WHILE statement, the condition will be tested against before each potential iteration. If it evaluates to false the loop will stop and if it evaluates to true then the loop will continue with one more iteration before testing the condition again.incrementExpression
: This will be run after each iteration of the loop. It’s normally used to increment a counter variable (e.g.counter++
) but any expression is suitable.
Performance:
As far as speed and performance goes, the same rules apply! Anything used within the statement or within the condition must be cached if it’s a property (i.e. something you would access.like.this):
// BAD: for (var i = 0; i < myArray.length; i++) { foo.bar.special.method ( myArray[i] ); } // GOOD: for (var i = 0, fn = foo.bar.special.method, length = myArray.length; i < length; i++) { fn ( myArray[i] ); } // Better, In my opinion: var i = 0, fn = foo.bar.special.method, length = myArray.length; for (; i < length; i++) { fn ( myArray[i] ); } |
The last chunk of code does exactly the same as the second (‘GOOD’) except the variables are declared outside the FOR statement making it a little more readable.
‘break’ & ‘continue’
First, it’s important for you to know about ‘labels’. JavaScript allows you to label a loop, this is optional but can be very useful when dealing with nested loops. Here’s an example:
myOuterLoop : while (condition) { myInnerLoop : while (condition) { // Do something... } } |
A label is not a variable or property, it’s simply a way to reference one loop from another using either the ‘break’ or ‘continue’ statement.
The ‘break’ statement:
Using ‘break’ within a loop will exit the current iteration of that loop and will prevent it from continuing.
It’s similar to using return false
within a function in that you will usually use it only if some condition is or isn’t met. For example:
while (condition) { var success = doSomething(); if (!success) { break; } } |
If the condition, !success
evaluates to true then the loop will completely stop, if however the condition is NOT met then the loop will continue. Another example:
var alpha = ['A','B','C','D','E'], i = 0, length = alpha.length, resultString = ''; for (; i < length; i++) { if (alpha[i] === 'D') break; resultString += alpha[i]; } // resultString === 'ABC' |
To break one loop from inside another you would need to label the outside loop. An example:
myOuterLoop : while (condition) { myInnerLoop : while (condition) { if (whatever) { break myOuterLoop; } if (whatever2) { break; // Same as 'break myInnerLoop;' } } } |
The ‘continue’ statement:
This statement stops the current iteration of a loop but the loop will continue with other iterations. Essentially, it restarts the loop: (it doesn’t reset the counter variable though, so the loop will continue)
You use it in the same fashion as the ‘break’ statement.
var alpha = ['A','B','C','D','E'], i = 0, length = alpha.length, resultString = ''; for (; i < length; i++) { if (alpha[i] === 'D') continue; resultString += alpha[i]; } // resultString === 'ABCE' // The 'D' is skipped... |
And, you can use labels to reference other loops:
myOuterLoop : while (condition) { myInnerLoop : while (condition) { if (whatever) { continue myOuterLoop; } if (whatever2) { continue; // Same as 'continue myInnerLoop;' } } } |
Other constructs
Do..while loop:
In addition to the WHILE loop you can also use the DO…WHILE loop. The only difference with it is that it will execute something before it checks (the condition) to execute it again, unlike the regular WHILE loop which executes something only after it’s checked the condition:
do { something(); } while (theCondition); // something() will be executed at least once, // even if theCondition evaluates to false. |
Functional looping:
I don’t really know what to call it but since it’s centered around using functions I think “functional looping” suits quite nicely.
Take a look:
var myArray = ['Apple','Orange','Lemon']; (function(i){ doSomethingWith ( myArray[i] ); // Recall anonymous function, // and increment counter: if (myArray[i+1]) arguments.callee(i+1); })(0); |
While it may be slower than regular looping constructs it can be useful in certain situations, especially if you want to delay the continuation of the loop, e.g.
(function(i){ doSomethingWith ( myArray[i] ); // Recall anonymous function after 100ms if (myArray[i+1]) setTimeout(function(){ arguments.callee(i+1); }, 100); })(0); |
Thanks for reading…
Your loops are faster now, no?
I hope this information is useful to someone. I’m really no good at this documentation-style writing but hey, at least I’m trying! If you have any questions please ask in the comments!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
cool!
will keep this somewhere in my mind now …
I saw a little typo: bigObject.smallProp.lentgh
Good tutorial
This is just great.
I am a full time PHP developer. But want to become much much much better in javascript. Used loops so many times, but never ever thought about performance and ease of use to such a limit. I am a fan of ur small but big snippets.
Just want to take ur suggestions on what to follow to learn much more about javascript. I dont want to blindly adopt jquery or other JS frameworks. Jus want to learn the basics to the best.
Also I am following you tuts on javascript DOM series and waiting for part 2.
Thanks for the comments! I’m glad you all found it helpful.
@Vasili, thanks, I just corrected it.
@Swapnil, good for you, so many people use jQuery and other frameworks without knowing what’s really going on under the surface. Btw Part 2 of that series should be out sometime next week.
Really awesome post! Thanks James.
Excellent article; I had no idea that decrementing a while loop would have such an effect. Also great point on the use of variable caching, I especially like the idea of caching the function call.