0

This has bothered me for a while, see my jsfiddle: http://jsfiddle.net/DHR8Q/, which contains the following javascript code:

var id = "11111;22222;33333";

id = id.split(";");

alert(typeof id);

for (var i in id){
    alert(id[i]);
}​

When I split the variable id by the character ";", then I use the for loop, I would assume that i == 0,1,2 because the string id was split into 3 parts, but this is not what happens. In reality i == 0,1,2,3,4.... Why does JavaScript do this?

jeffery_the_wind
  • 17,048
  • 34
  • 98
  • 160
  • This is confusing. Maybe a JS "pattern" ? Why would you put the result of the split in the variable itself ? (I mean, in your question here ...) – Skippy Fastol Apr 20 '12 at 15:47
  • 1
    What I see is, it first alerts the three array items, then starts alerting each method from the array prototype. Why not use a regular `for` loop (not `for...in`)? See – bfavaretto Apr 20 '12 at 15:48

4 Answers4

11

Because the jsFiddle you linked to includes the Mootools library; which adds its own methods to the Array.prototype.

When you enumerate over an array using for in, it loops over all attributes; including those methods; which is what you're seeing.

You should instead check using hasOwnProperty(), or, much preferably use for(;;);

for (var i in id){
    if (id.hasOwnProperty(i)) {
        alert(id[i]);
    }
}​

(jsFiddle)

or

for (var i=0;i<id.length;i++) {
    alert(id[i]);
}

(jsFiddle)

Matt
  • 74,352
  • 26
  • 153
  • 180
4

This is because for... in iterates over all properties of an object (including those from the prototype).

When you for... in over an array, you get the array elements, as well as the length property, and any properties of Array.prototype.

You can fix this by using hasOwnProperty:

for (var i in id){
    if(id.hasOwnProperty(i){
        alert(id[i]);
    }
}​

Though, I suggest not using for... in for arrays, use forEach:

id.forEach(function(v){
    alert(v);
});

Or a good ol' for loop:

for(var i=0, len=id.length; i<len; i++){
    alert(id[i]);
}

EDIT

The forEach function is not available as a built-in on back-level browsers, so you can do this:

if (typeof Array.prototype.forEach !== 'function') {
    Array.prototype.forEach = function(fn, thisObj) {
        var scope = thisObj || window,  L = this.length, i;
        for (i=0; i < L; ++i) {
            fn.call(scope, this[i], i, this);
        }
    };
}
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • @bfavaretto: `forEach` works in Chrome 7+, Firefox 3.5+, and IE 9+. http://kangax.github.com/es5-compat-table/ – gen_Eric Apr 20 '12 at 15:51
  • 3
    forEach may not be supported as a builtin in your browser, but it is easy to define the extension method. – Cheeso Apr 20 '12 at 15:51
  • Yes, I suggest using the new ES5 methods, and just [shimming](https://github.com/kriskowal/es5-shim) them into old browsers. – gen_Eric Apr 20 '12 at 15:53
  • Thanks for the link. I was mistaking it for [`for each...in`](https://developer.mozilla.org/en/JavaScript/Reference/Statements/for_each...in), which is non-standard. – bfavaretto Apr 20 '12 at 15:53
  • @bfavaretto: I've never seen that before. – gen_Eric Apr 20 '12 at 15:54
  • 2
    Me neither, until a colleague recently used it and broke our app for most users... – bfavaretto Apr 20 '12 at 15:56
2

Arrays in JS are objects. When you do a for...in on an array, you get all the indices as well as all the prototype members of that object. In your case the prototype members included a bunch of array functions.

You should use a regular for loop to iterate over numeric indices.

This output from the JSShell in emacs illustrates:

js> 
Multi-line input. Use two consecutive blank lines to eval.

var id = "11111;22222;33333"; 

id = id.split(";"); 

js> function say (x) {WScript.echo(x);}
js> for (var i in id) { say (i); } 
0
1
2
forEach
every
map
filter
remove
removeAt
indexOf
contains
js> for(var i=0;i<id.length;i++) { say (i + ": " + id[i]); } 
0: 11111
1: 22222
2: 33333
js>

see also: JavaScript Loops: for...in vs for

Community
  • 1
  • 1
Cheeso
  • 189,189
  • 101
  • 473
  • 713
1

You shouldn't use this for arrays because firstly order is not guaranteed, and secondly these are used to list all enumerable properties of an object which may not be desirable for Arrays as several frameworks add properties to the Array prototype which will also get enumerated.

Mig56
  • 131
  • 1
  • 6