Why do arrays behave this way when I add named properties? - javascript

I did following experiment in browsers console
I created a new Array.
Added "foo" to the Array as named index "name".
Added "bar" to the Array using push method.
4 & 5 are tests on the Array
1. var myArray = []; // undefined
2. myArray["name"] = "foo"; // "foo"
3. myArray.push("bar"); // 1
4. myArray.join(", "); // "bar"
5. myArray["name"]; // "foo"
My questions (what I didn't understand)
.push() returns 1 which is the length of the Array, but it must be 2 as the Array has two values "foo" & "bar"
Test 4 shows that the Array has only one value "bar" but test 5 oppose it showing that it also has a value "foo".
Why doesn't Array methods (push, join etc) works on key/value pairs ?
Then how does associative Array works and how we can handle it(methods, properties etc).

.push() returns 1 which is the length of the Array, but it must be 2 as the Array has
two values "foo" & "bar"
No, because arrays in JavaScript are not associative data structures (even though you can attach arbitrary properties to them). The only items that count as "array contents" are those whose property names satisfy certain conditions.
You should also be aware that length may also report a number greater than what you expect if the array has "holes". For example:
var a = [1, 2, 3];
console.log(a.join(" "), a.length); // "1 2 3", 3
delete a[1];
console.log(a.join(" "), a.length); // "1 3", still 3!
Test 4 shows that the Array has only one value "bar" but test 5 oppose it showing that it also has a value "bar".
That's irrelevant. The array also has many other properties, but join will only touch those mentioned earlier.
Why doesn't Array methods (push, join etc) works on key/value pairs ?
Because that's what the spec says.
Then how does associative Array works and how we can handle it(methods, properties etc).
Use a plain object instead of an array if you want string keys.

JavaScript doesn't really have associative arrays. Being able to do myArray["foo"] = "bar" is a side effect of arrays simply being objects, and objects having properties.
Only numeric properties count as elements of the array. If you do myArray["foo"] = "bar" you are not adding an element to the array, you're simply adding a property to the object. That's different to doing myArray[0] = "bar" which is adding an element, and will accordingly update the length property of the array object.

In JavaScript you cannot address array elements with a string, only integers. So,
1. var myArray = []; // undefined
2. myArray["name"] = "foo"; // "foo"
3. myArray.push("bar");
myArray will only contain "bar" since it ignores myArray["name"] = "foo"; which is why you get length 1.

Just like functions, Arrays are objects. But there are many Array prototype functions which purposefully do not access the properties of object. There is no 'associative array' in JavaScript, other than object and arrays depending on the function or code can behave or be leveraged in the same way you would use an associative array. You will encounter the same confusion in for loops when iterating over object key/pairs vs array values. In general though they are quite effective, there is a good book called Java Script the good parts. Which also then highlights the features or 'bad parts' to avoid or are dangerous. Googling about the sharp edges of objects and arrays in Javascript yields some good reads. They are not complex, but you do have to understand the more subtle ways Java is not like JavaScript.

Arrays in Javascript are not meant to be associative - when you add a property via a key you're actually adding it to the array object, not to the array itself.
If you want a key:value pair, use an Object

Related

Difference between named and indexed arrays - javascript

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.

What is happening under the hood in javascript when using the Array brackets notation

I have generally found javascript to be transparent, in that there are very few black boxes where "magic" just happens and you should just accept and look the other way, however I have not found any answer to how the Array brackets [] notation actually works under the hood.
let arr = [4, 5, 6, 7]
console.log(arr[3]) // <- How does this work?
What is javascript doing to access the item at index 3. Does it internally call some method on the Array.prototype?
With an object, the [] is a shortcut for a property accessor.
let obj = {
a: 'hello',
b: 'world'
}
obj['a'] === obj.a // true
Is an array then just an object with a long list of integer based properties?
let objArray = {
0: 'hello',
1: 'world'
}
let realArray = ['hello', 'world']
objArray[0] === 'hello' // true
realArray[0] === 'hello' // true
objArray.0 // SyntaxError: Unexpected number
realArray.0 // SyntaxError: Unexpected number
I have seen many many online discussions that all come to the conclusion that you cannot overload the brackets notation to truly subclass an Array but I have never seen an explanation on what magic is happening under the hood that allows the Array to work the way it does.
The obvious follow up question is whether there is any way to intercept the bracket notation access to define your own behavior, but I think I already know the answer to that.
You'd probably have to look at the implementation code to know precisely what's going on, but the basic idea is that arrays are actually layered atop objects.
This is backwards:
With an object, the [] is a shortcut for a property accessor.
The bracket notation is more fundamental. Thus, obj['foo'] and obj.foo work the same, but there is no equivalent for obj['foo & bar'], which is perfectly legitimate, and will respond with a value if obj has a key named "foo & bar".
Is an array then just an object with a long list of integer based properties?
Not quite, but you're not far off. Arrays are objects with the Array prototype, and with a little bit of additional magic to set the length property when new keys are added, or remove keys when that length is set.
And no, you cannot override the [] operator for your own purposes.
Is an array then just an object with a long list of integer based properties?
Yes, in it's simplest form, an Array is an Object with a list of integer base properties that is based on the Array prototype (which gives access to all the array methods like map, forEach, etc.)
As for intercepting the bracket notation, no, I have not seen anything that would allow that besides creating your own Object that has the methods you need (and then only access that object via the appropriate methods).
More info from MDN:
Arrays are list-like objects whose prototype has methods to perform traversal and mutation operations. Neither the length of a JavaScript array nor the types of its elements are fixed. Since an array's length can change at any time, and data can be stored at non-contiguous locations in the array, JavaScript arrays are not guaranteed to be dense; this depends on how the programmer chooses to use them. In general, these are convenient characteristics; but if these features are not desirable for your particular use, you might consider using typed arrays.
Arrays cannot use strings as element indexes (as in an associative array) but must use integers. Setting or accessing via non-integers using bracket notation (or dot notation) will not set or retrieve an element from the array list itself, but will set or access a variable associated with that array's object property collection. The array's object properties and list of array elements are separate, and the array's traversal and mutation operations cannot be applied to these named properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#Array_object

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

Create an associative array with integer keys

I need to create an associative array in javascript with an integer key as follows;
a["10"] = "ten";
but when i create an array, it puts the value to the 10th index of the array and it creates an array with the length 11. I want it to be a key value pair. I know this can be done by using objects but i need an array only.
JavaScript does not have associative arrays. The only way to do this in JavaScript is to use objects:
var a = {
'10': 'ten'
};
ECMAScript does have Associated Arrays1 - Objects (and by extension, Arrays) are an example
However, some properties of Arrays are treated specially:
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 ..
.. 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 ..
Thus, given arr = [], the expressions arr["1"] and arr[1] refer to the same property name. Since P (the property name) is "1" and length is 0 from above, then assignment to such property will set arr.length to ToUint32(P)+1, or 2.
It is not possible to change this behavior. If you wish to not have a special length property, then use a "normal" Object instead of an Array. However, many of the Array.prototype functions can be used with arbitrary objects (with some implementation quirks aside) that have a length property and an Object can be created such that it uses Array.prototype as its own prototype.
All that being said, the post does not say what the real issue is. Instead of supposing that it must be done in that particular manner, consider explaining what the intent is: e.g. why a["10"]? And what is wrong if there are "11 items" if the object will be used in a List?
1 Please read the article before debating this statement: the term "Array" in the name does not imply an ordered sequence nor does it preclude an additional notion of a Length or the use of Hashing, etc. If you are going by a different definition, make sure to specify what it is and what the desired behavior is for a given operation.

JavaScript Internals 101: arrays are just a special type of objects

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"!

Categories