How are JavaScript arrays represented in physical memory?

Here is some food for thought. I made a jsperf to test a simple array optimization that some JavaScript engines implement.

The test case creates two arrays with a million elements each. The a array contains only numbers; the b array contains the same numbers except for the first element which is an object:

var a = [ 0 ], b = [ { valueOf: function() { return 0; } } ];
for( var i = 1;  i < 1000000;  ++i ) {
    a[i] = b[i] = i;
}

The valueOf property of the object in the first element of the b array returns 0 so the arithmetic will be the same as the first array.

Then the two tests simply sum all the values for the two arrays.

Fast array:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += a[i];
}

Slow array:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += b[i];
}

As you can see from the test results in the jsperf, in Chrome the numeric array is about 5x faster, in Firefox the numeric array is about 10x faster, and in IE it's about 2x faster.

This doesn't directly reveal the internal structures used for the array, but it gives a pretty good indication that the two are quite different from each other.


Normally, arrays allocate a contiguous block of memory of fixed length. However, in Javascript, arrays are Object types with special constructors and accessor methods.

Which means, a statement like:

var arr = new Array(100000);

does not allocate any memory! In fact, it simply sets the value of the length property in the array. When you construct an array, you don't need to declare a size as they grow automatically. So, you should use this instead:

var arr = [];

Arrays in Javascript are sparse which means not all the elements in the array may contain data. In other words, only the elements that actually contain data exist in the array. This reduces the amount of memory used by the array. The values are located by a key and not by an offset. They're simply a method of convenience and not intended to be used for complex numerical analysis.

Arrays in Javascript are not typed so the value of an element can be an object, string, number, boolean, function or an array. The main difference between an array and an object is the length property which has a value greater than the largest integer key in the array.

For example:

You could have an create an empty array and add two elements at index 0 and index 99. The length would be 100, but the number of elements in the array would be 2.

var arr = [];
arr[0] = 0;
arr[99] = {name: "John"};
console.log(arr.length); // prints 100
arr; // prints something like [0, undefined × 98, Object { name: "John"}]

To answer your questions directly:

Q. It is my understanding that I can store mixed data in a JavaScript array, as well as change any element in the array to some other type. How does the interpreter keep track of what place in physical memory any element is at? Also, how is the overwriting of the data in the next element prevented if I change an element to a larger data type?

A. You probably know this by now if you've read my comments above. In Javascript, an array is a Hashtable Object type so the interpreter doesn't need to keep track of physical memory and changing the value of an element doesn't affect other elements as they're not stored in a contiguous block of memory.

--

Q. I assume that arrays only store references to actual objects, and primitives are wrapped behind the scenes when placed in arrays. Assuming this is the case, if I have a different handle on the primitive variable and change the value stored in the array is synchronicity maintained?

A. No, primitives are not wrapped. Changing a primitive that was assigned to an array will not change the value in the array as they're stored by value. Objects on the other hand are stored by reference, so changing the objects value will reflect that change in that array.

Here's an example you can try:

var arr = [];
var obj = { name: "John" };
var isBool = true;

arr.push(obj);
arr[1] = isBool;

console.log(arr[0]); // print obj.name
console.log(arr[1]); // print true

obj.age = 40;        // add age to obj
isBool = false;      // change value for isBool

console.log(arr[0]); // value here will contain age
console.log(arr[1]); // value here will still be true

Also, note that when you initialize an array in the following two ways, it has a different behavior:

var arr = new Array(100);
console.log(arr.length);        // prints 100
console.log(arr);               // prints []

var arr2 = new Array(100, 200);
console.log(arr2.length);       // prints 2
console.log(arr2);              // prints [100, 200]

If you want to use Javascript Arrays as contiguous blocks of memory, you should look into using TypedArray. TypedArray's allow you to allocate a block of memory as a byte array and access the raw binary data more efficiently.

You can learn more about the intricacies of Javascript by reading the ECMA-262 spec (ver 5.1).