JavaScript For Loops 101





5.00/5 (4 votes)
All you ever wanted to know about the for loop in JavaScript and all its variants
What?! JavaScript doesn't have a for-each loop? You gotta be kidding me!
That was my reaction to my co-worker whose code I was peer-reviewing.
Background
By their very nature, JavaScript's arrays are sparse. See this and this. My colleague had some extensive operations on a sparse array and I thought the performance can be improved by using a for-each loop instead of a for
loop to iterate over the array. But surely, he hasn't seen client side JavaScript code in the web world that uses a for-each loop.
So we sat down and did some research. And we came across this exhaustive discussion on StackOverflow. We realized that JavaScript has many variants of the for
loop. We had to refer to various other articles on the web to fully understand the concept and syntax. IMO they are not really advertised well enough for beginners. Which means a beginner would always try to mold his program to a vanilla for
implementation instead of using a variant that is more suitable to the situation. Also, what makes matters more complex is not all the variants are available in all environments (e.g., browsers, servers).
Seemed like some documentation was needed.
Introduction
This article intends to be an all-you-ever-wanted-to-know tutorial about the for
loop in JavaScript and all its variants.
A Word about ECMAScript, JavaScript and Feature Mismatch
JavaScript is an implementation of ECMAScript which is a set of specifications laid out in ECMA-262 and ISO/IEC 16262. So if you see this, you will notice that different browsers and different versions of these browsers support different versions of ECMAScript implementation. In other words, they support different versions of JavaScript. Which in turn means not all features of ECMAScript are available in all versions of JavaScript and not all features of JavaScript are available in all browsers. So one has to be careful writing code that works across environments and provide alternatives for environments that do not support certain features. We will discuss this aspect while talking about different flavors of the for
loop.
For a quick reference to which browser version supports which version of JavaScript and ECMAScript, see this.
So, tell me about all types of for loops!
Plain Old Vanilla for loop
Syntax
for (statement 1; statement 2; statement 3) {
//code block to be executed
}
Statement 1 is executed before the code block starts. Normally, you will use statement 1 to initiate the variable used in the loop (var index = 0
).
Statement 2 defines the condition for running the code block. Often, statement 2 is used to evaluate the condition of the initial variable.
Statement 3 is executed each time after the code block has been executed. Often, statement 3 increments or decrements the initial variable.
All the 3 statements are optional.
Example
var index;
var myArray = ["a", "b", "c"];
for (index = 0; index < myArray.length; ++index) {
console.log(myArray[index]);
}
Simple enough!
forEach
So they DO have a forEach loop after all!
But it is only supported by environments that are compliant to ECMAScript edition 5. That means IE 9 or later and Firefox 4 or later. See this.
Okay! Let's get the basics first.
Syntax
array.forEach(callback [, contextObject]){
//code block to be executed
}
Parameters
callback
: The callback
function to execute for each element (required)
contextObject
: Object to be used as a context for the callback
function (optional)
Example
var index;
var myArray = ["a", "b", "c"];
myArray.forEach(function(entry) {
console.log(entry);
});
For more examples, see this.
Why This is Better
The callback
function is automatically invoked with three arguments: the value of the element, the index of the element and the Array
object being traversed. Which means you don't have to declare indexing and entry variables in the current scope, as they're supplied as arguments to the iteration function. So their scope is limited to just that iteration. We will use an example to understand this.
In a classic for
loop:
var myArray = ["a", "b", "c"];
for (var i = 0; i < myArray.length; i++) {
setTimeout(function() {
console.log(myArray[i]);
}, 500);
}
But it doesn't work. Why?!
Because, the variable scoping rules are different in JavaScript from languages like C#.
- There is no concept of block-scope in JavaScript. A "
var
" variable is accessible from everywhere inside the function where it was defined. This is function scope. If it is created outside of a function, it’ll become a global variable. In our case, when thei < myArray.length
condition stops the loop, the value ofi
is actually3
. - JavaScript passes the arguments of a function as references. When the
console.log(myArray[i])
is called, it uses the referenced value ofi
, which still exists within the scope, because a function created in a function can access the references of the parent function. setTimeout()
is anasync
method and it waits the specified number of milliseconds, and then executes the specified function. By this time, thefor
loop has finished and the value ofi
is3
at that time, so whenconsole.log()
is finally executed, it prints the value ofelements[3] - undefined
- three times.
Now, there is a workaround to this:
for (var i = 0; i < myArray.length; i++) {
(function(index) {
setTimeout(function() {
console.log(myArray[index]);
}, 500)})(i);
}
This works as expected. But is it really easy to understand? Compare this with the forEach
version below.
myArray.forEach(function(myArray) {
setTimeout(function() {
console.log(myArray);
}, 500);
});
Much better and readable code. You can focus more on your logic than bothering about the mechanics of the iteration, i.e., how to iterate through an array, how to handle scopes.
You may be concerned about the impact on performance in making a function call for each array entry. This article shows why you shouldn't be. In fact, some of it may be more than made up for by the fact that the forEach()
version uses less memory, because the very same function is used in each iteration. Whereas in the for
loop, we create a new function every time, and these stay alive until their scope ends.
Warning
As mentioned earlier, this may not work in older browsers such as Internet Explorer 8. But no fret. There is an easy workaround.
Insert the following code at the beginning of your scripts. This algorithm is exactly the one specified in ECMA-262, 5th edition, assuming Object
and TypeError
have their original values and that callback.call
evaluates to the original value of Function.prototype.call
.
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisArg */)
{
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++)
{
if (i in t)
fun.call(thisArg, t[i], i, t);
}
};
}
for...in
Now this one is a bit tricky. You have to handle it carefully!
T. J. Crowder has written the most comprehensive guide to for...in.
A for...in
loop only iterates over enumerable properties. It DOES NOT loop through the indexes of an array. Try to print it in your mind as best as you can. Before I elaborate further, let's get the basics right.
Syntax
for (variable in object) {
//code block to be executed
}
Parameters
variable
: A different property name is assigned to variable on each iterationobject
: Object whose enumerable properties are iterated
Example
var myObject = {prop1:1, prop2:2, prop3:3};
function show_props(obj, objName) {
var result = "";
for (var prop in obj) {
result += objName + "." + prop + " = " + obj[prop] + "\n";
}
return result;
}
console.log(show_props(myObject, "myObject"));
Output:
myObject.prop1 = 1
myObject.prop2 = 2
myObject.prop3 = 3
Okay!
Now back to the caveat that we were discussing. for
..in
loops through the enumerable property names of an object, not the indexes of an array. For example, this is WRONG!
var myArray, index;
myArray = ['a', 'b', 'c'];
for (index in myArray) {
console.log("myArray[" + index + "] = " + myArray[index]);
}
Output
myArray[0] = a
myArray[1] = b
myArray[2] = c
The output is all right. But this won't be the same in all environments and hosts. So, this is not the right usage. You ask why? Well, that is not what for
...in
is designed for.
In the example (right after syntax) above which shows the loop that went through each of the properties of the object myObject
- there is no guarantee whatsoever in which order these properties will be read. Arrays are nothing but objects in JavaScript and array indexes are nothing but property names. Any other property added to the prototype of the object or prototype of the object's prototype all the way up to the top of the chain of inheritance will all be listed using for
...in
.
In other words, you cannot add anything to myArray
or myArray
's prototype or its prototype's prototype, etc. if you want the 2nd example of this section to work in a predictable manner. If you are a pro JavaScript programmer, you would know that in the real world, you will not be able to live with this restriction for too long.
Fair enough! I get it! I will not use for...in to loop through index of an Array; instead I will use it loop through all the enumerable properties of an object. But you just said any other property added to the prototype of the object or prototype of the object's prototype all the way up to the top of the chain of inheritance will all be looped through? I don't want that!
Correct. There is a way to stop this. And that is the key to the correct usage of for
...in
.
var myArray = {a:1, b:2, c:3};
function ParentArray() {
this.color = "red";
}
ParentArray.prototype = myArray;
function show_own_properties(obj, objName) {
var result = "";
for (var prop in obj) {
if( obj.hasOwnProperty( prop ) ) {
result += objName + "." + prop + " = " + obj[prop] + "\n";
}
}
return result;
}
p = new ParentArray();
console.log(show_own_properties(p, "p")); /* alerts: o.color = red */
console.log(show_own_properties(myArray, "myArray"));
/* alerts: myArray.a = 1
myArray.b = 2
myArray.c = 3 */
hasOwnProperty
function is built into all objects. It tells us whether the property is on the object itself (returns true
), rather than being inherited from the object's prototype (returns false
). This deals with any enumerable properties that might have been added to Array.prototype
.
Also, notice this code will always access only the properties that are present. For an Array
such as this:
var a = [];
a[0] = "item1";
a[100] = "item2";
a[500] = "item3";
It will only access 3 elements and loop 3 times; not loop through 501 times. That is why it is more suitable than a vanilla for
loop in cases of sparse Arrays. And as we know from the beginning that JavaScript Arrays are sparse by their nature.
for...of
This is part of the Harmony (ECMAScript edition 6) proposal. So if you decide to use it, first check the browser compatibility here.
This variant of for
loop is essentially an implicit usage of iterators (see Further Reading section). While for
...in
iterates over property names, for
...of
iterates over property values:
Syntax
for (variable of object) {
//code block to be executed
}
Parameters
variable
: On each iteration, a value of a different property is assigned to variableobject
: Object whose enumerable properties are iterated
Example
The following example shows the difference between a for
...of
loop and a for
...in
loop.
let arr = [ 3, 5, 7 ];
arr.foo = "hello";
for (let i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // logs "3", "5", "7"
}
Under the covers, that gets an iterator from the array and loops through it. It uses an iterator defined by the object (the array), and arrays define that their iterators iterate through their entries (not their properties).
Reverse for Loop
This is not really a new feature or construct, but just looping backwards in a vanilla for
loop.
Example
for (var i=array.length; i--; ) {
//code block to be executed
}
OR
for (var i=array.length; i-->0 ;) {
//code block to be executed
}
See this nice post on it. It takes a bit of getting used to but is quite handy in certain situations. You don't need to declare a temporary length variable here or compare against Array.length
on each iteration, both of which might be minute optimizations though. There is another discussion on it which debates at length about the performance benefits of it.
Further Reading
- Explicit iterator (part of ES6+)
- If you are not familiar with the prototype property in JavaScript, then read this.
History
- 21st May 2014: v1