Javascript Array index fundamentals - javascript

I am not sure how the Javascript engines (specifically browser engines) store an array.
For example - how much memory would this use?
var x = new Array(0, 1, 2, 1000, 100000000);
I want to map integer dates as array indexes, but I need to be sure it isn't a bad idea.

Arrays are "special" in only a couple ways:
They've got some interesting array-like methods from their prototype ("slice()" etc)
They've got a "magic" length property that tracks the largest numeric property "name"
If you store something at position 10299123 in a brand-new array, the runtime does not use up all your memory allocating an actual, empty array. Instead, it stores whatever you want to store and makes sure that length is updated to 10299124.
Now the problem specifically with dates, if you're talking about storing the timestamp, is that (I think) they're bigger than 32-bit integers. Array indexes are limited to that size. However, all that means is that length won't be correct. If you don't really care about any of the array stuff anyway, then really all you need is a plain object:
var dateStorage = {};
dateStorage[someDate.getTime()] = "whatever";
JavaScript objects can be used as name-value maps as long as the name can be represented as a string (which is clearly true for numbers).

Related

Why can't we provide size of array in Javascript?

Why can't we provide size of array in JavaScript?
I mean even if it is possible why don't we why we just simply define the array.
Because standard arrays in JavaScript aren't really arrays at all (spec | post on my blog), they're just objects backed by Array.prototype with special handling for a class of property names ("array indexes"), a special length property, and a built-in literal notation. They aren't contiguous blocks of memory as in some other languages (barring optimization, of course).
I have a question in my mind about why can't we provide size of array in JavaScript ??
You can create an array with a given length via Array(n) where n is the length as a number. But again, it doesn't preallocate memory for that many slots or anything. You just end up with a sparse array with length set to n and no entries in it:
var a = Array(42);
console.log(a.length); // 42
console.log(0 in a); // false, it doesn't have an entry 0
a.forEach(function(entry) { // Never calls the callback
console.log(entry); // because the array is empty
});
I mean even if it is possible why don't we why we just simply define the array.
Because it serves no purpose.
Now, for typed arrays (Uint8Array and similar), we do indeed create them with a specific length (var a = new Uint8Array(42);), and that length is fixed (cannot change), because they're true arrays.
You can provide size of array. If its not given, you can add multiple values dynamically.
var arr = new Array(5);
You can provide size of array in java-script.
Java-script array is different from array in C language.
You can read more on following link
understanding-javascript-arrays
You can provide a size of an array and that size of an array will not change in the program.
Array is an object backed by Array.prototype, so there is a function called seal.
var myArray = Object.seal([5, 6, "saurabh", "text"]); // this is an array of size 4 fixed.
//myArray.push('new text'); //throw exception error
console.log(myArray[2]); //"saurabh"
myArray[0] = "change text";
console.log("print myArray: ", myArray);
You can read more over here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal

Is it an antipattern to set an array length in JavaScript?

Is it bad to use code like:
var a = [1,2,3,4];
a.length = 2; // 3 and 4 are removed
Does it have decent browser support? Do the removed values get garbage collected properly?
Does it have decent browser support?
Yes. This has been present since the very first edition of ECMAScript:
Specifically, 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.
Standard ECMA-262, 15.4
In ECMAScript 5, this was moved to 15.4.5.2.
Attempting to set the length property of an Array object to a value that is numerically less than or equal to the largest numeric property name of an existing array indexed non-deletable property of the array will result in the length being set to a numeric value that is one greater than that largest numeric property name.
Standard ECMA-262, 15.4.5.2
All browsers support this behavior.
Do the removed values get garbage collected properly?
Yes. (See quotes above, and 15.4.5.1.)
Is it an antipattern to set array length in Javascript?
No, not really. While arrays are "exotic objects" in ES6-speak, the real crazily unique thing about arrays are their indexing, not setting the length property. You could replicate the same behavior with a property setter.
It's true that property setters with non-subtle effects are somewhat unusual in JS, as their side-effects can be unobvious. But since .length has been there in Javascript from Day 0, it should be fairly widely understood.
If you want an alternative, use .splice():
// a is the array and n is the eventual length
a.length = n;
a.splice(n, a.length - n); // equivalent
a.splice(n, a.length); // also equivalent
If I were avoid setting .length for some reason, it would be because it mutates the array, and I prefer immutable programming where reasonable. The immutable alternative is .slice().
a.slice(0, n); // returns a new array with the first n elements
Arrays are exotic objects.
An exotic object is any form of object whose property semantics differ in any way from the default semantics.1
The property semantics for arrays are special, in this case that changing length affects the actual contents of the array.
An Array object is an exotic object that gives special treatment to array index property keys [...] 2
The behavior of the specific property key for this array exotic object is outlined as well.
[...] Every Array object has a length property whose value is always a nonnegative integer less than 2^32. The value of the length property is numerically greater than the name of every own property whose name is an array index; whenever an own property of an Array object is created or changed, other properties are adjusted as necessary to maintain this invariant [...]
This section is detailing that the length property is a 32 bit integer. It is also saying that if an array index is added, the length is changed. What this is implying is that when an own property name that is not an index is used, the length is not changed and also that names which are not indexes are considered numerically less than the indexes. This means that if you have an array and also add a string (not implicitly numeric either, as in not "3") property to it, that changing the length to delete elements will not remove the value associated with the string property. For example,
var a = [1,2,3,4];
a.hello = "world";
a.length = 2; // 3 and 4 are removed
console.log(a.hello);//"world"
[...] Specifically, whenever an own property is added whose name is an array index, the value of the length property is changed, if necessary, to be one more than the numeric value of that array index; [...]
In addition to the truncation, expansion is also available by use of an index. If an index value is used (an integer basically) then the length will be updated to reflect that change. As arrays in JavaScript are sparse (as in, no gaps allowed) this means that adding a value for a larger index can make an array rather larger.
var a = [1,2,3,4];
a[50] = "hello";
console.log(a.length);//51
[...] and whenever the value of the length property is changed, every own property whose name is an array index whose value is not smaller than the new length is deleted.
Finally, this is the specific aspect in question which the OP raises. When the length property of an array is modified, it will delete every index and value which is numerically more than the new length value minus 1. In the OP's example, we can clearly see that changing the length to 2 removed the values at index 2 and 3, leaving only the first two values who had index 0 and 1.
The algorithm for the deletion mentioned above can be found in the ArraySetLength(A, Desc) definition.
b. Let deleteSucceeded be A.[[Delete]](ToString(oldLen)). 3
Which is converting the index to a string and using the delete behavior for objects on the index. At which point the entire property is removed. As this internally reaches the delete call, there is no reason to believe that it will leak memory or even that it is an anti pattern as it is explicitly described in the language specification.
In conclusion, Does it have decent browser support? Yes. Do the removed values get garbage collected properly? Yes.
However, is it an anti-pattern? This could be argued either way. What it really breaks down to is the level of familiarity with the language of others who would be using the same code which is a nice way of saying that it may not have the best readability. On the other hand, since JavaScript does take up bandwidth or memory with each character used (hence the need for minification and bundling) it can be useful to take this approach to truncate an array. More often than not, worrying about this type of minutia is going to be a micro-optimization and the use of this special property should be considered on a case to case basis in context with the overall design of the related code.
1. The Object Type ECMA 6
2. Array Exotic Objects ECMA 6
3. ArraySetLength(A, Desc) ECMA 6
As it is writeable https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length (seen here) It should be okay to use it that way
You can set the length property to truncate an array at any time. When you extend an array by changing its length property, the number of actual elements does not increase; for example, if you set length to 3 when it is currently 2, the array still contains only 2 elements.
There's even an example how to shorten an array, using this on Mozilla Developer Network
if (statesUS.length > 50) {
statesUS.length = 50;
}
It's better to use slice because your intentions are clear.
var truncated = a.slice(0,2)
Slice does create a new array though.
If that is an issue for you then there is nothing wrong with modifying the length property.
It's actually the fastest way of truncation and supported in all browsers. jsperf
Is it an antipattern to set array length in JavaScript?
No. It would be OK to set it to truncate the array.
Does it have decent browser support?
Yes, see the compatiblity matrix here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length
Do the removed values get garbage collected properly?
Yes, they should. Will they actually get gced - you will need to profile the JavaScript code if this is a real concern.
This way of truncating an array is mentioned in JavaScript: Good Parts, so it is not an anti-pattern.
It is not an anti pattern. It is supported in all major browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length

Non-functionals of Arrays in JavaScript [duplicate]

The difference between a JavaScript Array, and Object is not very big. In fact it seems Array mainly adds the length field, so you can use both Arrays and Objects as numeric arrays:
var ar = new Array();
ar[0] = "foo";
ar["bar"] = "foo";
var ob = new Object();
ob[0] = "foo";
ob["bar"] = "foo";
assert(ar[0] == ob[0] == ar["0"] == ob["0"] == ar.bar == ob.bar); // Should be true.
So my questions is, in popular JavaScript engines (V8, JavaScriptCore, SpiderMonkey, etc.), how is this handled? Obviously we do not want our arrays to be actually stored as hash maps with key values! How can we be reasonably sure our data is stored as an actual array?
As far as I can see there are a few approaches engines could take:
Array is implemented exactly the same way as Object - as an associative array with string keys.
Array is a special case, with a std::vector-like array backing the numeric keys, and some density heuristic to prevent insane memory use if you do ar[100000000] = 0;
Array is the same as Object, and all objects get a heuristic to see if using an array would make more sense.
Something insanely complicated that I haven't thought of.
Really this would be simpler if there were a proper array type (cough WebGL typed arrays cough).
In SpiderMonkey, arrays are implemented basically as C arrays of jsvals. These are referred to as "dense arrays". However, if you start doing un-array-like things to them -- like treating them like objects -- their implementation is changed to something which very much resembles objects.
Moral of the story: when you want an array, use an array. When you want an object, use an object.
Oh, a jsval is a sort of variadic type which can represent any possible JavaScript value in a 64 bit C type.
In V8 and Carakan (and presumably Chakra), all (non-host) objects (both those that are arrays and those that aren't) with properties whose names are array indexes (as defined in ES5) are stored as either a dense array (a C array containing some value wrapper) or a sparse array (which is implemented as a binary search tree).
The unified object representation shows through in that it affects enumeration order: with an object, SpiderMonkey and SquirrelFish both give all properties in insertion order; and with an array, they in general (there are special cases in SM at least!) array indexes first then all other properties in insertion order. V8, Carakan, and Chakra always give array indexes first then all other properties in insertion order, regardless of object type.

Which takes less memory: a Javascript array or Javascript object?

If I have a Javascript list which will have only numeric keys, which takes less memory?
var array = [];
array[0] = 'hello';
array[5] = 'world';
array[50] = 'foobar';
var obj = {};
obj[0] = 'hello';
obj[5] = 'world';
obj[50] = 'foobar';
I don't know a ton about Javascript engine internals, so...
The reason I ask is because that array, when converted to a string, will have a bunch of undefined's in the middle of it. Are those actually stored in some fashion, or is that just put in at string conversion?
An array is basically an ordered set of values associated with a single variable name.
In your example I think you try to do an associative array, and you should use object, Array is not meant to be used for key/value pairs.
Also the array length is indirecly increased when you assign a value to an index with higher length of the current array length:
var array = new Array();
array[99] = "Test";
// array.length is now 100
Check this detailed article on the subject.
Probably the Javascript array because you can 'only' use numeric key values, where as the object literals provide a space for key values, and even if you use numerical key values, they are probably handled differently than the numerical key values for arrays.
Most likely the reason arrays can't have text-based key values are because they are treated differently than object literals. I'm guessing that because they are probably treated differently, the processing for the array probably is more optimized for numeric key values, were as a object literal is optimized to use strings or numbers as their keys.
JavaScript doesn't implement arrays like other languages so you don't get any performance enhancements inherent of a normal array (memory-wise); in JavaScript an array is very similar to an object; actually, it is essentially an object just with a few extra methods and capabilities (such as a length that updates itself). I'd say neither is quicker.

How are JavaScript arrays implemented?

Namely, how does the following code:
var sup = new Array(5);
sup[0] = 'z3ero';
sup[1] = 'o3ne';
sup[4] = 'f3our';
document.write(sup.length + "<br />");
output '5' for the length, when all you've done is set various elements?
My 'problem' with this code is that I don't understand how length changes without calling a getLength() or a setLength() method. When I do any of the following:
a.length
a['length']
a.length = 4
a['length'] = 5
on a non-array object, it behaves like a dict / associative array. When I do this on the array object, it has special meaning. What mechanism in JavaScript allows this to happen? Does JavaScript have some type of property system which translates
a.length
a['length']
into "get" methods and
a.length = 4
a['length'] = 5
into "set" methods?
Everything in JavaScript is an object. In the case of an Array, the length property returns the size of the internal storage area for indexed items of the array. Some of the confusion may come into play in that the [] operator works for both numeric and string arguments. For an array, if you use it with a numeric index, it returns/sets the expected indexed item. If you use it with a string, it returns/sets the named property on the array object - unless the string corresponds to a numeric value, then it returns the indexed item. This is because in JavaScript array indexes are coerced to strings by an implicit toString() call. Frankly, this is just one more of those things that makes you scratch your head and say "JavaScript, this, this is why they laugh at you."
The actual underlying representation may differ between browsers (or it may not). I wouldn't rely on anything other than the interface that is supplied when working with it.
You can find out more about JavaScript arrays at MDN.
Characteristics of a JavaScript array
Dynamic - Arrays in JavaScript can grow dynamically .push
Can be sparse - for example, array[50000] = 2;
Can be dense - for example, array = [1, 2, 3, 4, 5]
In JavaScript, it is hard for the runtime to know whether the array is going to be dense or sparse. So all it can do is take a guess. All implementations use a heuristic to determine if the array is dense or sparse.
For example, code in point 2 above, can indicate to the JavaScript runtime that this is likely a sparse array implementation. If the array is initialised with an initial count, this could indicate that this is likely a dense array.
When the runtime detects that the array is sparse, it is implemented in a similar way to an object. So instead of maintaining a contiguous array, a key/value map is built.
For more references, see How are JavaScript arrays implemented internally?
This really depends on what you intend to do with it.
[].length is "magical".
It doesn't actually return the number of items in the array. It returns the largest instated index in the array.
var testArr = []; testArr[5000] = "something"; testArr.length; // 5001
But the method behind the setter is hidden in the engine itself.
Some engines in some browsers will give you access to their implementations of those magic-methods.
Others will keep everything completely locked down.
So don't rely on defineGetter and defineSetter methods, or even, really, __proto__ methods, unless you know which browsers you know you're targeting, and which you aren't.
This will change in the future, where opt-in applications written in ECMAScript Next/6 will have access to more.
ECMAScript 5-compliant browsers are already starting to offer get and set magic methods in objects and there's more to come... ...but it's probably a while away before you can dump support for oldIE and a tonne of smartphones, et cetera...
It is important to know that when you do sup['look'] = 4; you are not using an associative array, but rather modify properties on the object sup.
It is equivalent to sup.look = 4; since you can dynamically add properties on JavaScript objects at any time. sup['length'] would for an instance output 5 in your first example.
To add to tvanfosson's answer: In ECMA-262 (the 3.0 specification, I believe), arrays are simply defined as having this behavior for setting properties (See 15.4.5.1). There's no general mechanism underlying it (at least as of now) - this is just how it's defined, and how JavaScript interpreters must behave.
As other people have mentioned, a property in JavaScript can basically act as both as getter and a setter of your array (or string or other inputs).
As a matter of fact, you might try this yourself:
const test = [1, 2, 3, 4, 5]
test.length = 3
console.log(test) // [1, 2, 3]
test.length = 5
console.log(test) // Guess what happens here!
As far as I know, arrays in JavaScript do not work exactly like associative arrays and you have elements which are put in memory as contiguously as possible (given that you can have arrays of mixed objects), depending on the JavaScript engine you are considering.
As a side note, I am a bit baffled that the most voted answer keeps spreading the over-simplified myth (or half-truth) of "everything being an object in JavaScript"; that is not exactly true, otherwise you will never study primitives, for example.
Try to do this:
const pippi = "pippi"
pippi.cat = "cat"
console.log(pippi.cat) // Will it work? Throw an error? Guess why again
Spoiler: the string is wrapped in a throwaway object for that specific operation on the second line, and then in the following one you are just going to access a property of the primitive which is not there (provided you did not play with String.prototype or the like), so you get undefined.
Array object inherits caller, constructor, length, and name properties from Function.prototype.
A JavaScript array is an object just like any other object, but JavaScript gives it special syntax.
arr[5] = "yo"
The above is syntactic sugar for
arr.insert(5,"yo")
which is how you would add stuff to a regular object. It's what is inside the insert method that changes the value of arr.length
See my implementation of a customArray type here: http://jsfiddle.net/vfm3vkxy/4/

Categories