Matt Murphy Matt Murphy - 3 months ago 8
Node.js Question

JavaScript arrays: string indexed items

I've had a bit of a wakeup to the nature of JavaScript array indexes recently. Pursuing it, I found the following (I'm working with Node.js in interpretive mode here):

var x=[];
x['a']='a';
console.log(x); // Yields [ a: 'a' ]
console.log(x.length); // yields 0 not 1
x[1]=1;
console.log(x); // Yields [ , 1, a: 'a' ]
console.log(x.length); // Yields 2 not 3 (one for empty 0 space, one for the occupied 1 space)


Is
a: 'a'
really what it looks like - an object property embedded in an array - and, thus, isn't counted in the array property
.length
?

Answer

In JavaScript, arrays are just objects with some special properties, such as an automatic length property, and some methods attached (such as sort, pop, join, etc.). Indeed, a will not be counted in your array, since the length property of an array only stores the amount of elements with a property name that can be represented with a 32-bit positive integer.

And since arrays always automatically define every numbered element up to the highest element with a positive 32-bit int property name, this effectively means the length property stores 1 higher than the element with the highest 32-bit integer as a property name. Thanks @Felix Kling for correcting me about this in the comments.

Adding properties such as a is not forbidden at all, but you should watch out with them, since it might be confusing when reading your code.

There's also a difference in walking through the elements in the array:

To walk through all the numbered elements:

for (var i=0; i<myArray.length; i++) {
    //do something
}

To walk through every property that's not built-in:

for (var i in myArray) {
    //do something
}

Note that this loop will also include anything that's included from Array.prototype that's not a built-in method. So, if you were to add Array.prototype.sum = function() {/*...*/};, it will also be looped through.

To find out if the object you're using is indeed an array, and not just an object, you could perform the following test:

if (Object.prototype.toString.call(myObject) === '[object Array]') {
    //myObject is an array
} else if (typeof myObject === 'object') {
    //myObject is some other kind of object
}

See @artem's comment: myObject instanceof Array might not always work correctly.

Comments