Monday, June 4, 2012

JavaScript for…in vs for


Do you think there is a big difference in for...in and for loops? What kind of "for" do you prefer to use and why?



Let's say we have an array of associative arrays:




var myArray = [{'key': 'value'}, {'key': 'value1'}];



So we can iterate:




for (var i = 0; i < myArray.length; i++)



And:




for (var i in myArray)



I don't see a big difference. Are there any performance issues?


Source: Tips4all

17 comments:

  1. The choice should be based on the which idiom is best understood.

    An array is iterated using:

    for (var i = 0; i < a.length; i++)
    //do stuff with a[i]


    An object being used as an associative array is iterated using:

    for (var key in o)
    //do stuff with o[key]


    Unless you have earth shattering reasons, stick to the established pattern of usage.

    ReplyDelete
  2. Douglas Crockford recommends in JavaScript: The Good Parts (page 24) to avoid using the for in statement.

    If you use for in to loop over property names in an object, the results are not ordered. Worse: You might get unexpected results; it includes members inherited from the prototype chain and the name of methods.

    Everything but the properties can be filtered out with .hasOwnProperty. Javascript: The Good Parts presents this code sample which does what you probably wanted originally:

    for(var name in obj)
    {
    if (obj.hasOwnProperty(name))
    {
    }
    }

    ReplyDelete
  3. FYI - jQuery Users



    jQuery's each(callback) method uses for( ; ; ) loop by default, and will use for( in ) only if the length is undefined.

    Therefore, I would say it is safe to assume the correct order when using this function.

    Example:

    $(['a','b','c']).each(function() {
    alert(this);
    });
    //Outputs "a" then "b" then "c"


    The downside of using this is that if you're doing some non UI logic, your functions will be less portable to other frameworks. The each() function is probably best reserved for use with jQuery selectors and for( ; ; ) might be advisable otherwise.

    ReplyDelete
  4. there are performance differences depending on what kind of loop you use and on what browser.

    For instance:

    for (var i = myArray.length-1; i >= 0; i--)


    is almost twice as fast on some browsers than:

    for (var i = 0; i < myArray.length; i++)


    However unless your arrays are HUGE or you loop them constantly all are fast enough. I seriously doubt that array looping is a bottleneck in your project (or for any other project for that matter)

    ReplyDelete
  5. Note that the native Array.forEach method is now widely supported.

    ReplyDelete
  6. I second opinions that you should choose the iteration method according to your need. I would suggest you actually not to ever loop through native Array with for in structure. It is way slower and, as Chase Seibert pointed at the moment ago, not compatible with Prototype framework.

    There is an excellent benchmark on different looping styles that you absolutely should take a look at if you work with JavaScript. Do not do early optimizations, but you should keep that stuff somewhere in the back of your head.

    I would use for in to get all properties of an object, which is especially useful when debugging your scripts. For example, I like to have this line handy when I explore unfamiliar object:

    l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);


    It dumps content of the whole object (together with method bodies) to my Firebug log. Very handy.

    ReplyDelete
  7. The two are not the same when the array is sparse.

    var array = [0, 1, 2, , , 5];

    for (var k in array) {
    // Not guaranteed by the language spec to iterate in order.
    alert(k); // Outputs 0, 1, 2, 5.
    // Behavior when loop body adds to the array is unclear.
    }

    for (var i = 0; i < array.length; ++i) {
    // Iterates in order.
    // i is a number, not a string.
    alert(i); // Outputs 0, 1, 2, 3, 4, 5
    // Behavior when loop body modifies array is clearer.
    }

    ReplyDelete
  8. here is something i did.

    function foreach(o, f) {
    for(var i = 0; i < o.length; i++) { // simple for loop
    f(o[i], i); // execute a function and make the obj, objIndex available
    }
    }


    this is how you would use it
    this will work on arrays and objects( such as a list of HTML elements )

    foreach(o, function(obj, i) { // for each obj in o
    alert(obj); // obj
    alert(i); // obj index
    /*
    say if you were dealing with an html element may be you have a collection of divs
    */
    if(typeof obj == 'object') {
    obj.style.marginLeft = '20px';
    }
    });


    I just made this so I'm open to suggestions :)

    ReplyDelete
  9. Updated answer for 2012 current version of all major browsers - Chrome, Firefox, IE9, Safari and Opera support ES5's native array.forEach.

    Unless you have some reason to support IE8 natively (keeping in mind Chrome frame can be provided to these users, which will provide a proper JS environment), it's cleaner to simply use the language's proper syntax:

    myArray.forEach(function(item) {
    console.log(item);
    });


    Full documentation is at MDC, who maintain Javascript: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach

    ReplyDelete
  10. I'd use the different methods based on how I wanted to reference the items.

    Use foreach if you just want the current item.

    Use for if you need an indexer to do relative comparisons. (I.e. how does this compare to the previous/next item?)

    I have never noticed a performance difference. I'd wait until having a performance issue before worrying about it.

    ReplyDelete
  11. For in loops on Arrays is not compatible with Prototype. If you think you might need to use that library in the future, it would make sense to stick to for loops.

    http://www.prototypejs.org/api/array

    ReplyDelete
  12. With for (var i in myArray) you can loop over objects too, i will contain the key name and you can access the property via myArray[i]. Additionaly, any methods you will have added to the object will be included in the loop, too, i.e., if you use any external framework like jQuery or prototype, or if you add methods to object prototypes directly, at one point i will point to those methods.

    ReplyDelete
  13. If you really want to speed up your code, what about that?

    for( var i=0,j=null; j=array[i++]; foo(j) );


    it's kinda of having the while logic within the for statement and it's less redundant. Also firefox has Array.forEach and Array.filter

    ReplyDelete
  14. I have seen problems with the "for each" using objects and prototype and arrays

    my understanding is that the for each is for properties of objects and NOT arrays

    ReplyDelete
  15. Using forEach to skip the prototype chain

    Just a quick addendum to @nailer's answer above, using forEach with Object.keys means you can avoid iterating over the prototype chain without having to use hasOwnProperty.

    var Base = function () {
    this.coming = "hey";
    };

    var Sub = function () {
    this.leaving = "bye";
    };

    Sub.prototype = new Base();
    var tst = new Sub();

    for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
    }

    Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
    });

    ReplyDelete
  16. Use the Array().forEach loop to take advantage of parallelism

    ReplyDelete
  17. Watch out!

    If you have several script tags and your're searching an information in tag attributes for example, you have to use .length property with a for loop because it isn't a simple array but an HTMLCollection object.

    https://developer.mozilla.org/en/DOM/HTMLCollection

    If you use the foreach statement for(var i in yourList) it will return proterties and methods of the HTMLCollection in most browsers!

    var scriptTags = document.getElementsByTagName("script");

    for(var i = 0; i < scriptTags.length; i++)
    alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

    for(var i in scriptTags)
    alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!


    Even if getElementsByTagName should return a NodeList, most browser are returning an HTMLCollection:
    https://developer.mozilla.org/en/DOM/document.getElementsByTagName

    ReplyDelete