20

I just want to understand how Javascript arrays work but I have a complicated problem here.

First I created my array:

var arr = [];

And set some elements in it:

arr[5] = "a thing";
arr[2] = undefined;

I thought that I should have an array of size 2, because I only have two objects at 2 specific indexes. So I tested it with the .length property of arrays:

document.write(arr.length + "<br>");

The result, interestingly, is 6. But it must contain two items. How can its size be 6? It is probably related with the latest index that I used, here arr[5] = "a thing";

I then tried to loop over it:

var size = 0;
for(var x in arr){
 size++;   
}

And the size variable is now 2. So, what I learned from this: if I use a for in loop, I will calculate how many properties are in it, not its last index.

But if I try to document.write(arr[4]) (which is not set yet), it writes undefined.

So why is arr[2] counted in the for..in loop, but not arr[4]?

Let me answer my question: what I was thinking about typeof undefined == undefined which is amazingly true. But this is JavaScript, we need to play with it using his own rules :)

jsFiddle and snippet below.

var arr = [];

arr[5] = "a thing";
arr[2] = undefined;
document.write(arr.length + "<br>");

var size = 0;
for(var x in arr){
 size++;   
}

document.write(size + "<br>");

document.write(arr[4] + "<br>");
Ahmet Can Güven
  • 4,734
  • 4
  • 31
  • 49
  • The *length* property is always **at least** one larger than the largest index. It's self–adjusting. – RobG Jul 05 '15 at 22:41
  • 4
    Important note: *do not use `document.write`*, it does not do what you think it does. What it does **not** do, for instance, is "print some data". It's an extremely low level function, and modern JS has tons of better ways to do what in 1998 we needed document.write for. – Mike 'Pomax' Kamermans Jul 05 '15 at 22:41
  • @Mike'Pomax'Kamermans, yes I will not next time. All, thanks for the all answers. – Ahmet Can Güven Jul 05 '15 at 22:43
  • @Mike'Pomax'Kamermans See also http://stackoverflow.com/a/802894/ – guest271314 Jul 05 '15 at 22:55
  • Possible duplicate of http://stackoverflow.com/questions/22448330/javascript-in-operator-for-undefined-elements-in-arrays – Salman A Jul 06 '15 at 07:45
  • No sir, these questions are totally different. Please try to read before comment. My main question here is why undefined is not counted in for in loop but the ones I set are counted. And your question is totally different. – Ahmet Can Güven Jul 06 '15 at 08:31
  • @AhmetCanGüven `typeof undefined == "undefined"` return true. Also `arr[4]` is not defined while `arr[2]` has for value `undefined` (not really the same thing). – Hacketo Jul 06 '15 at 08:52
  • @Hacketo Sorry my mistake, I was going to write true too. – Ahmet Can Güven Jul 06 '15 at 09:36
  • @AhmetCanGüven it is exactly the same as [this](http://stackoverflow.com/questions/22448330/javascript-in-operator-for-undefined-elements-in-arrays). Even the accepted answers are nearly identical. – Salman A Jul 06 '15 at 09:43
  • @SalmanA No sir i don't think it is similar, I never saw that topic and I also looked for solutions before asking my question. – Ahmet Can Güven Jul 06 '15 at 09:51
  • "we need to play with it using his own rules" - how is that different from other languages? :o – BartoszKP Jul 06 '15 at 12:01
  • May I criticize the question title? I would say this is about undefined *values*, *items* or *elements* -- not indexes. I came here wondering about the semantics of `array[undefined] = 'foo';` – Chris K Jun 05 '18 at 08:31

6 Answers6

12

Note: Array indexes are nothing but properties of Array objects.

Quoting MDN's Relationship between length and numerical properties section,

When setting a property on a JavaScript array when the property is a valid array index and that index is outside the current bounds of the array, the engine will update the array's length property accordingly.

Quoting ECMA Script 5 Specification of Array Objects,

whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted

So, when you set a value at index 5, JavaScript engine adjusts the length of the Array to 6.


Quoting ECMA Script 5 Specification of Array Objects,

A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 232−1.

So, in your case 2 and 4 are valid indexes but only 2 is defined in the array. You can confirm that like this

arr.hasOwnProperty(2)

The other indexes are not defined in the array yet. So, your array object is called a sparse array object.

So why arr[2] is counted in for..in loop and not arr[4] is not counted?

The for..in enumerates all the valid enumerable properties of the object. In your case, since only 2 is a valid property in the array, it will be counted.

But, when you print arr[4], it prints undefined, because JavaScript will return undefined, if you try to access a property which is not defined in an object. For example,

console.log({}['name']);
// undefined

Similarly, since 4 is not yet defined in the arr, undefined is returned.


While we are on this subject, you might want to read these answers as well,

Community
  • 1
  • 1
thefourtheye
  • 206,604
  • 43
  • 412
  • 459
8

There’s a difference between a property that has the value undefined and a property that doesn’t exist, illustrated here using the in operator:

var obj = {
    one: undefined
};

console.log(obj.one === undefined); // true
console.log(obj.two === undefined); // true

console.log('one' in obj); // true
console.log('two' in obj); // false

When you try to get the value of a property that doesn’t exist, you still get undefined, but that doesn’t make it exist.

Finally, to explain the behaviour you see: a for in loop will only loop over keys where that key is in the object (and is enumerable).

length, meanwhile, is just adjusted to be one more than whatever index you assign if that index is greater than or equal to the current length.

Ry-
  • 199,309
  • 51
  • 404
  • 420
4

To remove undefined values from array , try utilizing .filter()

var arr = [];
arr[5] = "a thing";
arr[2] = undefined;
arr = arr.filter(Boolean);
document.write(arr.length);
guest271314
  • 1
  • 10
  • 82
  • 156
3

It all comes down the idea of how space is handled by machines. Let's start with the simplest idea of:

var arr =[];

This in turn creates a location where you can now store information. As @Mike 'Pomax' Kamermans pointed out: This location is a special javascript object that in turn functions as a collection of keys and values, like so:

arr[key] = value;

Now moving on through your code:

arr[5] = "a thing";

The machine now is understanding that you are creating something in the (giving value to the) 6th position/5th key (as array's first position is 0). So you wind up with something that looks like this:

arr[,,,,,"a thing"];

Those commas represent empty positions (elisions as @RobG pointed out) in your array.

Same thing happens when you declare:

arr[2] = undefined;
arr[,,undefined,,,"a thing"];

So when you're iterating inside an array using "for var in" you're checking for each one of the spaces in this array that are populated, so in turn 2.

As a difference, when you check for the length of the array, you're looking to see how many spaces to store information exist inside the array, which in turn is 6.

Finally, javascript interprets empty room in an array as unidentified values, which is the reason arr[4] is being outputted as such.

Hope that answered your question.

  • 1
    This is simply not true for JS. The JS "array" is just a JS object (the one you know as `{}`) but with some special iteration functions, and special key-valye handling. Setting `arr = []` and then `arr[5] = "something"` does NOT create a something at "the 6th position", it just binds the property "something" to the object key `5`. The array `length` will report 6, but the array itself will contain *1* element. – Mike 'Pomax' Kamermans Jul 05 '15 at 22:42
  • "*empty positions*" in an array literal are [*elisions*](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-initializer) and are represented by commas: `[,,,,,"a thing"]`. Such members are said to be *elided*, they don't exist and return *undefined* when accessed for read. – RobG Jul 05 '15 at 22:43
  • @Mike'Pomax'Kamermans can't not agree with what you're saying, in the sense that it is semantically correct for javascript. You're right, arrays have keys and values, not positions. I'll edit my answer. Also RobG I was attempting to explain the concept in the simplest way posible, also figured out underscores where easier to use as a representation. Either way I can't disagree with you semantics. – Orlando Paredes Hamsho Jul 05 '15 at 22:45
2

JavaScript arrays, at least in the first version, were plain object with a length property. Most of the weird behaviour you experienced is a consequence of this.

Result interesting, it is 6. But it must contain two data, how its size can be 6? It is probably related with the latest index that I used here arr[5] = "a thing";

It results in 6 because the length is always 1 higher than the highest index, even if there are actually fewer items in the array.

o why arr[2] is counted in for..in loop and not arr[4] is not counted?

because when you are doing:

arr[2] = undefined;

You are actually adding a key called 2 to the array object. As result, the value arr[2] is counted in the for in loop, while the a[4] is ignored.

Giuseppe Pes
  • 7,070
  • 3
  • 41
  • 80
0

The assignment sets the property of the array, so that when you do the for var i in style for loop, you only see properties that have been set (even if you set them to be undefined). When you assign a new integery property such as arr[6] the array modifies the length of the array to be 7. The memory underlying the array may or may not be reallocated accordingly, but it will be there for you when you go to use it - unless your system is out of memory.

Edited according to RobG's comment about what ECMA-262 says.

Catalyst
  • 2,751
  • 12
  • 17