Saturday 26 September 2020

JavaScript Arrays Oddities

As almost everything in the language, Arrays in JavaScript are not exactly the same as in other languages. In principle one thinks of an array as a continuous section of memory, where each item has the same size and hence it can be easily located based on its numeric index (you know: startAddress + (index * itemSize))... Well, this is not always the case in JavaScript.

First, in JavaScript arrays can be sparse (rather than dense). This means that if you create an array of a given size (e.g.: new Array(200);) but you only assign a value to some of those 200 positions, the unassigned ones are "holes" and do not take up space. Accessing one of those holes will retun an undefined value, traversing the array with for-of will also return undefined values, but traversing it with .forEach will skip the "holes". If we really had "holes" in the array (rather than having those positions pointing to "undefined") the Array can not be just a continuous piece of memory... Well, this article explains how it's implemented in the v8 engine. You can read there that for small arrays, a real array (continuous memory space) is used, with those empty spaces containing a "hole" value, so there's not any saving in memory space. If the array is big, then "a low level array" is no longer used, but a dictionary where the numeric indexes are the keys.

The article stops there, but there are more oddities with arrays. If you are familiar with Python (or with the last C# addition, indexes) maybe you have tried to use a negative index with your array. That sort of works in JavaScript, but not as you would expect. array[-1] does not refer to the last item in the array, but to a "-1" property (indeed as if you use any other string). So doing ar[-1] = "ax"; will add a property to the object:

 

> let ar = ["a","b"];
> ar;
[ 'a', 'b' ]
> ar[-1];
undefined
> ar[-1] = "Bonjour";
'Bonjour'
> ar[-1];
'Bonjour'
> ar;
[ 'a', 'b', '-1': 'Bonjour' ]
> 

So in a case like that I don't know how it is implemented. Maybe the "normal" part of the Array is maintained as a low level array and a dictionary is used only for the non numeric indexes, or maybe a dictionary is used for both numeric and non numeric indexes.

One additional comment. Both numeric and non numeric indexes are considered as "own properties" of the array. However, holes in the sparse array are not "own properties".

 

> ar;
[ 'a', 'b', '-1': 'Bonjour' ]
> ar.hasOwnProperty(1);
true
> ar.hasOwnProperty(-1);
true
> ar.hasOwnProperty(-2);
false

> let sparseAr = new Array(5);
> sparseAr[2] = "aa";
> sparseAr;
[ <2 empty items>, 'aa', <2 empty items> ]
> sparseAr.hasOwnProperty(0);
false
> sparseAr.hasOwnProperty(2);
true


No comments:

Post a Comment