Question

I was editing Chrome's sample oauth contacts extension

when I came across an interesting for-loop in line 7 of contacts.js:

for (var i = 0, contact; contact = contacts[i]; i++) {

variable i was never used in the body of the for loop, so it seemed like a typical "for...in" loop. I tried replacing the for-loop with

for (contact in contacts) {

but when I ran the extension, all my contacts came back undefined

Here is the full for-loop from the extension

for (var i = 0, contact; contact = contacts[i]; i++) {
  var div = document.createElement('div');
  var pName = document.createElement('p');
  var ulEmails = document.createElement('ul');

  pName.innerText = contact['name'];
  div.appendChild(pName);

  for (var j = 0, email; email = contact['emails'][j]; j++) {
    var liEmail = document.createElement('li');
    liEmail.innerText = email;
    ulEmails.appendChild(liEmail);
  }

  div.appendChild(ulEmails);
  output.appendChild(div);
}
Was it helpful?

Solution

What the code given does

What that does is evaluate what contacts[i] is and whether it is truthy or not, while at the same time caches the array element of the applicable index.

It's equivalent to the following code (note that in this example ++i has the same side effect as i++):

for (var i = 0; contacts[i]; ++i)
{   var contact = contacts[i];
    // use contact
}

This could be interpreted as something like the following:

  • If !contacts[i] is false (i.e. it is truthy) continue the loop.
  • Otherwise, end the loop (it is falsy).

If the goal of that code was to iterate through all of an array, the problem would be that if you wanted to iterate through an element but it was falsy, it would end the entire loop instead of performing the (likely) intended effect. Take this example:

var foo = [1, 3, 5, 7, 9, 0, 2, 4, 6, 8];

// example for-loop given
for (var i = 0; foo[i]; ++i)
{   var bar = foo[i];
    console.log('example: ' + bar);
}

// "normal" way of iterating through array
for (var i = 0, l = foo.length; i < l; ++i)
{   var bar = foo[i];
    console.log('normal: ' + bar);
}

You'd find that the example only logs up to the number 9, while the "normal" way goes through the entire array. Of course though, if you could guarantee that all values within the array would be truthy (e.g. all array elements are objects), then this isn't that much of an issue.

What for-in does and why it doesn't work

You tried to replace that code with the following:

for (contact in contacts) { /*code here*/ }

However, this doesn't work for a number of reasons:

  1. contact is a string of the property name, not the value of it. Take this example:

    var foo =
        { bar1: 1
        , bar2: 2
        , bar3: 3
        , bar4: 4
        , bar5: 5 };
    for (var i in foo) console.log(i);
    

    What you get back is the property name (i.e. "bar1, bar2...") instead of the value. To do so for an object, you'd have to do something like the following:

    for (var i in foo)
    {   var bar = foo[i];
        console.log(bar);
    }
    

    Now you should get back "1,2,3,4,5" on separate lines. If you got that, and some other things, you might be have defined items on Object.prototype - which is why it's generally a bad idea, unless it really makes the code cleaner, and there is a substantial purpose for doing so. To filter these out, add a hasOwnProperty() check:

    for (var i in foo) if (foo.hasOwnProperty(i))
    {   var bar = foo[i];
        console.log(bar);
    }
    

    The upcoming version of ECMAScript (the "standard" version of JavaScript, minus the DOM) will have something called for-of loops, which will make it easier to do this sort of thing.

  2. For-in loops generally aren't meant for arrays (it is possible, but it's just not a good idea usually). If you need to use for-in, you probably should be using an object instead - all arrays are objects, just that arrays have special internal length property and a few other things.

  3. contact is an implied global, big no-no. In fact, implied globals are banned in strict mode. Use a variable declaration (inside or outside the for-in loop, doesn't matter) to solve this issue.

It's just learning about how JavaScript works and where to apply its various methods of doing things - some are more suitable than others in particular situations.

OTHER TIPS

Here you are using an array,not an object.

Though using for..in outputs the same result as a normal for loop,this would be my answer.

MyRecommendation:

Use for..in for iterating over objects:

for..in iterates over properties of an object.

Note:the order of iteration is arbitary.

var Myobj = {
    a: 1,
    b: 2,
    c: 3
};

for ( var prop in Myobj ) {
    console.log(prop);      // a...b...c
    console.log(Myobj[prop]); // 1...2...3
} 

but with this the problem is it will continue searching for enumerable properties up the prototype chain.So unless you dont use hasOwnProperty,it will iterate over local object and the prototype it is attached to.

 //Improved version of above code:
for (var prop in Myobj) {
    if ( Myobj.hasOwnProperty(prop) ) {
        // prop is actually obj's property (not inherited)
        console.log(prop);      // a...b...c
        console.log(Myobj[prop]); // 1...2...3
    }
}

Use for loop for iteration over an array

for loop iterates over an array in sequential way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top