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.
Related
When using an array constructor and pushing a few values into it as well as adding named values there are some interesting differences.
let arr = []
arr.push('a')
arr.push('b')
arr.push('c')
arr.foo1 = 'bar1'
arr.foo2 = 'bar2'
arr.forEach and for of iterates only over the indexed values, while for in iterates of the indexes/keyed values.
I guess what I'm wondering is why would anyone use this?
One thing that would be cool to have from both objects and array would be a guaranteed order of key-value pairs
(not just [{key:'value'}, {key2: 'value2'] but something where you could use for of on an object where the order would be based on the order in which the keys of the object were set. I know that's not possible but just suggesting something that would be nice to gain between both objects and arrays)
but It seems as though the order of keyed values is not able to be iterated over, so the order is not guaranteed (unless I'm wrong)
Doing this seems like it is a mix between an object and an array, but why would you mix when you can just create an object or array in a common manner?
Arrays are JavaScript objects like any other that you create. (Almost) everything in JavaScript inherits its prototype from Object.
Now the specification of array is such that the indexes are basically just specifically named properties. a[1] and a['1'] are pointing the same property. The only extra thing is that you have to have a length numeric property. In words of ECMAScript language specification, "Array objects are exotic objects that give special treatment to a certain class of property names".
Now, forEach is defined as a function that will only loop through those "special" properties that an Array instance has. It starts with 0 and finishing with length - 1, and it skips undefined values. It doesn't look at other props. So you only get fields in the array.
for of is an iterator - if you have an iterable object, you basically get its iterator function and then loop over values that it gives you. And an array iterable is giving only those indexed property names. You can read a bit more over at MDN.
Once again, an array is object like any other, but we give some of it's properties special meaning, and it inherits some functions from Array prototype that can deal with those properties.
But for in does not care if this object is special or not. It will simply loop over ALL properties of any object you give it, including an array. That's why when you for in loop the object, you're looping the object's properties, and array indexes are included into this.
Note: usage of for ... in ... is not recommended. E.g in a sparse array, (where you set, e.g. a[0] = 1, a[10] = 1, and leave all other indexes undefined), a for in would just log out those two properties.
So if somebody gives you an object that has properties called 0 and 10, would you call it an array? Nope. Another bad thing is that it will also go over the object's prototype and list out the properties of all the objects this array might have inherited from - and you likely do not want that.
Short answer: there is no such thing as a "named array" in JS.
JS's datatype model is based on objects, which are key/value pair containers, and arrays are a special kind of object with additional (native) logic for dealing with numerical keys.
As such, anything you can do to an object, you can do to an array, but you shouldn't: if you need named keys, use an object. If you need numerical keys with derived properties like length and a utility API like push/pop/shift/unshift, forEach, map, etc. use an array.
Also note that the length property says nothing about the actual array footprint in memory: arrays are not like C/Java/etc arrays at all, behaving more like vectors/arraylists instead: they're just JS objects with numerical key/pair bindings, so if you set an array[0] and then you set an array[99], the length will claim "100" and that number will mean nothing at all: your array, in memory, is just an object with a key/value pair keyed on the string 0 and a key/value pair keyed on the string 100.
This quora answer is pretty good further reading, explaining all of this based on the actual ECMAScript spec definitions.
Internally, JavaScript has only objects (and primitive types). Objects are unordered collection of key:value pairs, where key is string and value can be any JavaScript type.
Arrays, though on the outside look like normal arrays of any other language, are actually a special case of objects, with natively supported language syntax.
Each array is an object where the key is a number and value can be any type. The keys are managed by JavaScript itself so that we can manipulate the array as an ordered collection of values. This is the reason we can use arrays in a for-in loop too. Additionally, JavaScript provides standard array operations (length, indexOf, splice, slice, join) as methods that take use the numbered keys to do their thing.
>>> typeof []
"object"
>>> Object.prototype.toString.call([])
"[object Array]"
Is my understanding correct, or am I missing something?
One thing you missed up there is that the numeric indices themselves are simply named properties of the array object, there is no underlying order to them.
Another thing that might be worth keeping in mind is that the Array constructor can be modified, which will influence all arrays from that point on.
Consider the following:
var a = new Array(1,2,3,4,5);
a.slice(1); // returns [2,3,4,5]
//Now for some prototype modification
Array.prototype.slice = function(){alert("Foo");};
var b = new Array(1,2,3,4,5);
a.slice(1); // alerts "Foo"!
//The [] syntax is also affected
var c = [1,2,3,4,5];
c.slice(1); // alerts "Foo"!
I've been reading, and they're saying that associative arrays won't give you the same efficiency as arrays. An associative array can look things up in O(N) time, where an array can look things up in O(1).
Here's my question: which one would be more efficient in terms of looking up values quickly and not hogging too much memory?
Associative:
var myVars=new Array();
myVars['test1'] = a;
myVars['test2'] = b;
myVars['test3'] = c;
... (up to 200+ values)
echo myVars['test2'];
Stored Associative:
var myVars=new Array();
var TEST1 = 1;
var TEST2 = 2;
var TEST3 = 3;
... (up to 200+ values)
myVars[TEST1] = a;
myVars[TEST2] = b;
myVars[TEST3] = c;
... (up to 200+ values)
echo myVars[TEST2];
First, the first usage of Array is wrong. Although it is possible to do it, it does not mean you should. You are "abusing" the fact that arrays are objects too. This can lead to unexpected behaviour, e.g. although you add 200 values, myVars.length will be 0.
Don't use a JavaScript array as associative array. Use plain objects for that:
var myVars = {};
myVars['test1'] = a;
myVars['test2'] = b;
myVars['test3'] = c;
Second, in JavaScript there is no real difference between the two (objects and arrays). Arrays extend objects and add some behaviour, but they are still objects. The elements are stored as properties of the array.
You can find more information in the specification:
Array objects give special treatment to a certain class of property names. 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 both:
var obj = {'answer': 42};
obj['answer'];
and
var arr = [42];
arr[0];
have the same access time†, which is definitely not O(n).
†: It is better to say should have. Apparently this varies in different implementations.
Apart from that, your second example is horrible to maintain. If you assign numbers to variables, why not use the numbers directly?
var myVars = [];
myVars[0] = a;
myVars[1] = b;
myVars[2] = c;
Update:
More importantly: You have to choose the right data structure for your needs and this is not only determined by the access time of a single element, but also:
Are the keys consecutive numbers or arbitrary strings/numbers?
Do you have to access all (i.e. loop over all) elements of the collection?
Numerical arrays (arrays) and associative arrays (or hash tables/maps (objects in JS)) provide different solutions for different problems.
I posit that the present responses do not fully consider more practical use cases. I created this jsperf to demonstrate. While #Felix's jsperf demonstrates lookup speed, it's not performed on sufficiently large objects to be really useful. I think 10,000 simple properties is more reasonable. Further, you need to randomly select keys in the sequence to read, modify, delete and create to truly demonstrate the performance differences between the two types.
First of all, whoever they are, feel free to ignore them.
Every decent implementation of every decent scripting language, including JavaScript, will give you associative arrays that are either O(log(n)) access time, or else O(1) average access time, O(n) worst case (which you almost never hit). Either way in practice a lookup is fast.
Arrays have O(1) guaranteed access time, which is incredibly fast. But in some scripting languages (eg PHP) there isn't even a native array type provided. They just use associative arrays for both.
Answer: test it out yourself.
Update: After some back-and-forth with Felix, it appears that array access is usually faster than both associative arrays and objects. This is not always the case, notably in Chrome. In Chrome 11 on Ubuntu 11, arrays are faster. In Chrome 11 on Mac OS 10.6 there is no notable difference between them.
These tests did not measure manipulation, only reading.
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.
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/