2

I just tried the for...in statement in Javascript.

This gives no error:

var images = document.getElementsByTagName('img');

for(x in images){
    document.write(images[x]) + " ");
}

However, this does what it should but gives an error in the FF error console.

for(x in images){
    images[x].style.visibility="visible";
}

This made me VERY curious as to what's going on.

Doing this:

for(x in images){
    document.write(x);
}

...gave me this:

01234567891011121314151617lengthitemnamedItem

What's there at the end? I assume this makes the document.images / document.getElementsByTagName('img') array not suitable to use with the for...in statement since the values for x at the end won't correspond to an image? Maybe a for loop is better?

3 Answers 3

5

Don't iterate through arrays with for ... in loops. Use an index:

for (var i = 0; i < arr.length; ++i) {
  // images[i] ...
}

The for ... in construct isn't wrong, it's just not what you want to do; it's for when you want to iterate through all properties of an object. Arrays are objects, and there are other properties besides the semantically-interesting indexed elements.

(Actually what comes back from getElementsByTagName isn't really an Array; it's a node list. You can however treat it like an array and it'll generally work OK. The same basic caveat applies for for ... in in any case.)

Sign up to request clarification or add additional context in comments.

5 Comments

Mostly agree about the counting loop, but sometimes when dealing with sparse arrays, it's useful to use for..in -- you just have to know that you're not looping through array indexes but property names and act accordingly. :-)
Well sure - rules are meant to be broken, but probably somebody who's had to come here to ask the question should probably be pretty careful until getting some more experience ...
I see.. out of interest, how are the other properties in the array stored? Because I assume length should return the number of properties in the array, and even though there were more properties than index numbers (0-17), the array.length still returned 18 and not more (as would have been expected).
@fast-reflexes: length is defined as being the value of the highest numeric index plus one; it is not a count of properties on the array. The special handling of length is the only really special thing about arrays in JavaScript, in fact. length changes if you set a new numerically-named property that's higher than any previous ones, and setting length to a lower number automatically deletes the properties with numeric names that fall outside the range 0..length-1 inclusive.
Thanks a lot! Deeper knowledge acquired :)
1

for..in does not loop through the indexes of an array, it loops through the enumerable property names of an object. It happens that the only enumerable properties array instances have, by default, are array indexes, and so it mostly works to think it does array indexes in limited situations. But that's not what for..in does, and misunderstanding this will bite you. :-) It breaks as soon as you add any further properties to the array (a perfectly valid thing to do) or any library you're using decides to extend the array prototype (also a valid thing to do).

In any case, what you get back from document.getElementsByTagName isn't an array. It's a NodeList. Your best bet for iterating through NodeLists is to use an explicit index a'la Pointy's answer -- e.g., a straight counting loop:

var i;
for (i = 0; i < list.length; ++i) {
    // ... do your thing ...
}

Somewhat off-topic because it doesn't relate to your NodeList, but: When you are actually working with a real array, because arrays in JavaScript are sparse, there is applicability for for..in, you just have to be clear about what you're doing (looping through property names, not indexes). You might want to loop only as many times as the array has actual entries, rather than looping through all the indexes in the gaps in the sparse array. Here's how you do that:

var a, name;
a = [];
a[0] = "zero";
a[10000] = "ten thousand";
for (name in a) {
    // Only process this property name if it's a property of the
    // instance itself (not its prototype), and if the name survives
    // transition to and from a string unchanged -- e.g., it's numeric
    if (a.hasOwnProperty(name) && parseInt(name) == name) {
        alert(a[name]);
    }
}

The above only alerts twice, "zero" and "ten thousand"; whereas a straight counting loop without the checks would alert 10,001 times (mostly saying "undefined" because it's looping through the gap).

5 Comments

Very interesting indeed... would you mind briefly explaining var a,name, a=[] (a is array?)?
@fast-reflexes: Yes. a = []; is exactly the same as a = new Array();. Similarly, o = {}; is exactly the same as o = new Object(); It's just a briefer form using literal notation rather than an explicit call to the constructor function.
@fast-reflexes: It just declares two variables, a and name, exactly like var a; var name; but in fewer keystrokes. :-) It's probably worth getting a decent intro-to-JavaScript book and working through some exercises, etc. Enjoy!
Thanks mate! Yeah, it's such a creative world full of shortcuts :)
Cool, now after a few hours I understand this one... gave me some good insight about how this stuff works.. thanks again :)
0

The problem with the for ... in construct is that everything from the prototype(s) gets included in the enumeration.

What your output is showing is basically every attribute of images, which is an array object. So you get

  1. all the elements in your array, i.e. the numbers that appear in your output.
  2. all the properties available on your array. This is why you see length in your output for example.

Your code breaks due to number 2, since a functions does not have a style attribute

So, yes, as Pointy shows, the correct way to iterate over the elements of an array in JavaScript is by using a for loop.

1 Comment

Thanks, I now know a whole lot more about this than expected :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.