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
?
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
?
- Neato. Didn't even know this was possible. Makes sense though as an array is just an object. – iambriansreed Commented Dec 19, 2014 at 20:09
3 Answers
Reset to default 13In 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.
That's correct, and it's a good illustration of the fact that that new Array you've created is really just a special kind of Object. In fact, typeof []
is 'object'
! Setting a named property on it (which might be written more cleanly here as x.a = 'a'
) is really setting a new property to the object wrapper around your "real" array (numbered properties, really). They don't affect the length property for the same reason that the Array.isArray
method doesn't.
Yes, that's correct. This works because pretty much everything in JavaScript is an object, including arrays and functions, which means that you can add your own arbitrary string properties. That's not you say that you should do this.
Arrays ([]
) should be indexed using nonnegative integers. Objects ({}
) should be "indexed" using strings.