Click here to Skip to main content
15,878,852 members
Articles / Programming Languages / Javascript

JavaScript For Loops 101

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
21 May 2014CPOL9 min read 18.4K   10  
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

JavaScript
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

JavaScript
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

JavaScript
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

JavaScript
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:

JavaScript
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#.

  1. 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 the i < myArray.length condition stops the loop, the value of i is actually 3.
  2. JavaScript passes the arguments of a function as references. When the console.log(myArray[i]) is called, it uses the referenced value of i, which still exists within the scope, because a function created in a function can access the references of the parent function.
  3. setTimeout() is an async method and it waits the specified number of milliseconds, and then executes the specified function. By this time, the for loop has finished and the value of i is 3 at that time, so when console.log() is finally executed, it prints the value of elements[3] - undefined - three times.

Now, there is a workaround to this:

JavaScript
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.

JavaScript
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.

JavaScript
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

JavaScript
for (variable in object) {
    //code block to be executed
}

Parameters

  • variable: A different property name is assigned to variable on each iteration
  • object: Object whose enumerable properties are iterated

Example

JavaScript
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!

JavaScript
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.

JavaScript
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:

JavaScript
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

JavaScript
for (variable of object) {
  //code block to be executed
}

Parameters

  • variable: On each iteration, a value of a different property is assigned to variable
  • object: Object whose enumerable properties are iterated

Example

The following example shows the difference between a for...of loop and a for...in loop.

JavaScript
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

JavaScript
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

  1. Explicit iterator (part of ES6+)
  2. If you are not familiar with the prototype property in JavaScript, then read this.

History

  • 21st May 2014: v1

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
India India
I dabble at many things and many of them at the same time. In short, I am a jack of all trades.

I am a software engineer by profession. My current area of expertise include .net Programming, Analysis, Software Operations and Architecture. I am equally conversant with greenfield and brownfield projects.

By hobby, I am a Technology Enthusiast, Blogger, Arm chair Movie Critic and self-proclaimed Cricket Expert. I have assisted in reviewing quite a few technology books so far. Sometimes I get lucky and take nice pics when I have my D-SLR in my hand.

Hope you found my article useful.

Also check out my blogs here:
http://pandaxp.wordpress.com
http://coderpanda.blogspot.in

Comments and Discussions

 
-- There are no messages in this forum --