Javascript for...in statement gives error when looping through array - javascript

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?

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

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

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
all the elements in your array, i.e. the numbers that appear in your output.
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.

Related

Array type being picked up as array value

I have some simple Javascript looping through an array of items (Tridion User Groups) to check if the user is a member of a specific group.
I can easily code around the issue shown below ( see && extensionGroup !== 'true') but I want to understand why the isArray = true is counted as a value in the array - any ideas?
The screenshot below demonstrates that the value extensionGroups has been set thus
var extensionGroups = ["NotEvenARealGroup", "Author", "ExampleGroupAfterOneUserIsActuallyIn"];
but returns the isArray value as a 4th value?
updated to show images a little clearer
You're using for in to iterate an array; don't do that. Use for (or forEach):
for(var i = 0; i < extensionGroups.length; i++) {
var extensionGroup = extensionGroups[i];
// ...
}
The reason this fails is because for in is used to iterate over an object's properties in JavaScript. Iterating over an array in this way means you get anything else assigned to it, such as this property or length.
And if you're able to use Array#forEach, it's probably most appropriate here:
extensionGroups.forEach(function(extensionGroup) {
// ...
});
For..in, technically speaking, doesn't iterate through values. It iterates through property names. In an array, the values ARE properties, under the hood. So when you iterate over them with for..in you get funky stuff like that happening.
Which highlights my next point: don't use for..in. Don't use it for arrays -- don't use it for anything, really. Ok -- maybe that's going a bit too far. How about this: if you feel the need to use for..in, think hard to see if it's justifiable before you do it.

Monkeypatched code appearing in for...in loop over Array

I have the following code in my web app's main JavaScript:
// uniq for arrays
if (!Array.prototype.getUnique) {
Array.prototype.getUnique = function () {
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
}
It's a simple uniq clone for javascript, monkeypatched into the basic Array class. (Not here to debate monkeypatching, flame elsewhere please...)
getUnique() works as intended, but now whenever I iterate over an Array with a for...in loop, an additional index called getUnique is passed to the iterator body, and when this false n is looked up, getUnique's code is the value. (In other words: for...in is looping over the array as it should, but appending a getUnique as the last iteration)
What is going on here? There's another function monkeypatched in right above this one that works fine (indexOf()) and does not appear in the iterator. Here is some example code that triggers this issue:
for (var r in result) {
//tags = tags + result[r]["tags"].split(" ").join(", ")
if (result[r]["tags"]) {
var newtags = result[r]["tags"].split(" ");
debug.log(newtags);
for (var n in newtags) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
}
The debug output from this snippet looks like:
[14:22:26.090] [["camp", "furnitur", "wood"]]
[14:22:26.093] ["n is 0"]
[14:22:26.096] ["n is 1"]
[14:22:26.099] ["n is 2"]
[14:22:26.101] ["n is getUnique"]
What is going on here? Refactoring the monkeypatch and dropping it in a utility class is easy enough, but this is a really strange edge case to me. I have some ideas in my head about what is happening here, but those are just guesses. Can anyone explain?
The common misconception is that the for...in operation is, in javascript, made to iterate Arrays. This is wrong. It's usage is to iterate over any enumerable property of an object. In javascript, everything is an object (Arrays included). But you may notice, when you do for...in on an array, you don't get values out like length, slice, etc. This is because those are NOT enumerable properties of the object.
This SO question has some really good information on the usage of for...in: Why is using "for...in" with array iteration a bad idea?
If you want to stick to using for...in, you can do like was suggested above and check for hasOwnProperty inside your for...in
for ( var v in myArray ) {
if ( myArray.hasOwnProperty(v) ) {
// Do Something
}
}
I, however, suggest using plain old boring loops...
for ( var i = 0; i <= myArray.length - 1; i++ ) {
// Do Something
}
Without going into crazy detail, this works without hitting your added methods because the "indexes" of the array are not really indexes at all, but are instead properties with a name that matches their corresponding index: i.e 1, 0, 4, etc.
They feel like indexes because in javascript you can't access a property with dot notation if that property is a number (i.e: myArray.0 will not work). So you do myArray[0], and that feels like an array.
This is what hasOwnProperty is designed to solve: it tells you whether the name you've iterated to is on the object itself, or is inherited from the prototype.
Try this:
for (var n in newtags) {
if (newtags.hasOwnProperty(n)) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
You shouldn't iterate over an array using for…in loop, that is intended for enumerating properties of objects. So, for instance, you have no guarantee about the order of the indexes, neither – as you saw – that you're iterate only numeric indexes. If someone else adds a property to the Array's prototype in such way, you will iterate that property as well. See https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in to have the complete picture.
However, in your case, you can do the follow:
for (var n in newtags) {
if (newtags.hasOwnProperty(n)) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
That also prevent to iterate properties that are added by other scripts to the prototype. Also, you could define your function as not enumerable (in the browser where ES5 is supported):
Object.defineProperty(Array.prototype, "getUnique", {
value: function () { /*.. your function ..*/}
})
See : https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Don't use for-in for iteration of numeric properties.
Use a for loop:
for (var i = 0; i < newtags.length; i++) {
The for-in statement is almost never the right tool for this job, because...
It includes all properties, including non-numeric, out of range, and prototyped properties.
It makes no guarantee of the order of enumeration.
While you could do a hasOwnProperty check, there's no real benefit, and has some down-sides...
It slows down your iteration
It doesn't help with the order of enumeration issue
Sometimes you want a prototyped index (rare, but it happens). Using hasOwnProperty makes this impossible.

IE8 for...in enumerator

So I am using this in IE8:
var hi=["hi", "lo", "foo", "bar"];
for(i in hi){console.log(i)};
//WTF is that indexOf i value?
LOG: 0
LOG: 1
LOG: 2
LOG: 3
LOG: indexOf
undefined
In chrome and others, I'll just get 0-3, no mysterious "indexOf" thing. Why and what's the fix?
Don't use for...in for arrays. It's best to use the traditional for loop in that case.
The reason is because for...in looks at the array as an object, and therefore properties like indexOf or length may be included in the loop. The normal for loop only deals with numeric keys, so this problem is avoided.
On a side note, unwanted properties could show up when iterating over plain objects as well (as others have noted, properties you add to the object's prototype will show up). You can get around this by writing your for...in loops this way:
var obj = { ... };
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var item = obj[prop];
...
}
}
To be clear though: you still shouldn't use this method on arrays.
You're using the wrong type of loop for an array - for ... in ... will also include any enumerable properties of the object, which in your case includes the .indexOf() method.
Use this instead:
var i, n = hi.length;
for (i = 0; i < n; ++i) {
console.log(i, hi[i]);
}
Chrome and other up to date browsers implement ECMAScript 5, and correctly mark all built-in methods as non-enumerable properties.
This is happening because a script you are including on your page is adding the indexOf method to Array.prototype. This means all arrays inherit an indexOf method, which is good, since it means you can use that method even in IE8.
But, since there is no way to mark a property as non-enumerable in IE8, you will end up seeing it every time you enumerate over all the properties of the array, which is what you do in a for-in loop. You probably wanted a for loop instead.

JavaScript Loops: for...in vs for

I faced a strange behaviour in Javascript. I get
"Object doesn't support this property or method"
exception for the removeAttribute function in the following code:
var buttons = controlDiv.getElementsByTagName("button");
for ( var button in buttons )
button.removeAttribute('disabled');
When I change the code with the following, the problem disappears:
var buttons = controlDiv.getElementsByTagName("button");
for ( var i = 0; i < buttons.length; i++ )
buttons[i].removeAttribute('disabled');
What is the value of button inside the for...in?
Don't use for..in for Array iteration.
It's important to understand that Javascript Array's square bracket syntax ([]) for accessing indicies is actually inherited from the Object...
obj.prop === obj['prop'] // true
The for..in structure does not work like a more traditional for..each/in that would be found in other languages (php, python, etc...).
Javascript's for..in is designed to iterate over the properties of an object. Producing the key of each property. Using this key combined with the Object's bracket syntax, you can easily access the values you are after.
var obj = {
foo: "bar",
fizz: "buzz",
moo: "muck"
};
for ( var prop in obj ) {
console.log(prop); // foo / fizz / moo
console.log(obj[prop]); // bar / buzz / muck
}
And because the Array is simply an Object with sequential numeric property names (indexes) the for..in works in a similar way, producing the numeric indicies just as it produces the property names above.
An important characteristic of the for..in structure is that it continues to search for enumerable properties up the prototype chain. It will also iterate inherited enumerable properties. It is up to you to verify that the current property exists directly on the local object and not the prototype it is attached to with hasOwnProperty()...
for ( var prop in obj ) {
if ( obj.hasOwnProperty(prop) ) {
// prop is actually obj's property (not inherited)
}
}
(More on Prototypal Inheritance)
The problem with using the for..in structure on the Array type is that there is no garauntee as to what order the properties are produced... and generally speaking that is a farily important feature in processing an array.
Another problem is that it usually slower than a standard for implementation.
Bottom Line
Using a for...in to iterate arrays is like using the butt of a screw driver to drive a nail... why wouldn't you just use a hammer (for)?
for...in is to be used when you want to loop over the properties of an object. But it works the same as a normal for loop: The loop variable contains the current "index", meaning the property of the object and not the value.
To iterate over arrays, you should use a normal for loop. buttons is not an array but a NodeList (an array like structure).
If iterate over buttons with for...in with:
for(var i in a) {
console.log(i)
}
You will see that it output something like:
1
2
...
length
item
because length and item are two properties of an object of type NodeList. So if you'd naively use for..in, you would try to access buttons['length'].removeAttribute() which will throw an error as buttons['length'] is a function and not a DOM element.
So the correct way is to use a normal for loop. But there is another issue:
NodeLists are live, meaning whenever you access e.g. length, the list is updated (the elements are searched again). Therefore you should avoid unnecessary calls to length.
Example:
for(var i = 0, l = buttons.length; i < l, i++)
for(var key in obj) { } iterates over all elements in the object, including those of its prototypes.
So if you are using it and cannot know nothing extended Object.prototype you should always test obj.hasOwnProperty(key) and skip the key if this check returns false.
for(start; continuation; loop) is a C-style loop: start is executed before the loop, continuation is tested and the loop only continues while it's true, loop is executed after every loop.
While for..in should not generally be used for Arrays, however prior to ES5 there was a case for using it with sparse arrays.
As noted in other answers, the primary issues with for..in and Arrays are:
The properties are not necessarily returned in order (i.e. not 0, 1, 2 etc.)
All enumerable properties are returned, including the non–index properties and those on the [[Prototype]] chain. This leads to lower performance as a hasOwnProperty test is probably required to avoid inherited properties.
One reason to use for..in prior to ES5 was to improve performance with sparse arrays, provided order doesn't matter. For example, in the following:
var a = [0];
a[1000] = 1;
Iterating over a using for..in will be much faster than using a for loop, as it will only visit two properties whereas a for loop will try 1001.
However, this case is made redundant by ES5's forEach, which only visits members that exist, so:
a.forEach();
will also only iterate over two properties, in order.

what is for....in statement in javascript

anyone can explain how to use for...in statement in javascript. I had read the w3school article but i think it is not so clear.Below is the code, please explain this:
<html>
<body>
<script type="text/javascript">
var x;
var mycars = new Array();
mycars[10] = "Saab";
mycars[20] = "Volvo";
mycars[30] = "BMW";
for (x in mycars)
{
document.write(mycars[x] + "<br />");
}
</script>
</body>
</html>
A for in loop will iterate through every property in an object.
In your example, the x variable will cycle through every property in the mycars object.
If you add mycars.expensive = "Porsche";, it will find that too.
Note that, as stated by MDC, for in loops should not be used to loop through ordinary arrays:
Although it may be tempting to use
this as a way to iterate over an Array,
this is a bad idea. The
for...in statement
iterates over user-defined properties
in addition to the array elements, so
if you modify the array's non-integer
or non-positive properties (e.g. by
adding a "foo" property
to it or even by adding a method or
property to
Array.prototype), the
for...in statement will
return the name of your user-defined
properties in addition to the numeric
indexes. Also, because order of
iteration is arbitrary, iterating over
an array may not visit elements in
numeric order. Thus it is better to
use a traditional for loop with a numeric index when
iterating over arrays. Similar
arguments might be used against even
using for...in at all (at least
without propertyIsEnumerable()
or hasOwnProperty()
checks), since it will also iterate
over Object.prototype (which, though
usually discouraged, can, as in the
case of Array.prototype, be usefully
extended by the user where are no
namespacing concerns caused by
inclusion of other libraries which
might not perform the above checks on
such iterations and where they are
aware of the effect such extension
will have on their own use of
iterators such as for...in).
First you create an object with 3 items (and not an Array)
var mycars = new Object();
mycars[10] = "Saab";
mycars[20] = "Volvo";
mycars[30] = "BMW";
where 10, 20 and 30 are the object properties.
then you want to navigate through the object, visit all properties and display each value associated to a property.
This is where the [ for (variable in object) expression ] javascript construction intervenes:
The variable will be set to the first property of the object, then to the 2nd, then to the last. Try
for (v in mycars) alert(v);
to see how it works, and this as well
for (v in mycars) alert("Property: "+v+", value: "+mycars[v]);
The for ... in construction iterates over every element within the object on the right side of in. In your case, the block below the for statement is executed once for every car in mycars.
for in is a way of burying bugs for later generations to discover. As has been copiously pointed out, if applied to an Array, it will loop through all the elements of the array, and the length of the array, and whatever other data happens to be attached to the array. Applying it to an ordinary object is better, but you still should still filter the indices through hasOwnProperty.
Better to use a framework-provided function like jQuery's $.each

Categories