1

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?

nima
  • 7,796
  • 12
  • 36
  • 53
Jordan Klaers
  • 149
  • 11
  • 2
    Arrays are Objects in JavaScript. So doing `arr.foo1 = 'bar1'` creates a property on the `arr` object - it does not add an element to the array. – Randy Casburn Jun 11 '19 at 18:31
  • All JavaScript objects can have numeric, string, or Symbolic keys. By convention, arrays are usually treated as though they have just numeric keys, but there's nothing to prevent you from mixing and matching all key types. – p.s.w.g Jun 11 '19 at 18:33
  • thats fair, but can you think of an example or use case where you would want a variable created with the array constructor to also have named properties, rather than two separate variables, one as just the array and the other as just the plain object with its key values – Jordan Klaers Jun 11 '19 at 18:33
  • and you can only iterate over the number keys? is it common to mix the keys types in an array? – Jordan Klaers Jun 11 '19 at 18:34
  • You can not iterator of the the object properties as if they are array elements. They are not. You can see this in your sample if you include `arr.forEach(console.log);`. May I do that for you? – Randy Casburn Jun 11 '19 at 18:36
  • You can iterate over all keys using [`Object.keys`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) and its allies. And yes, [the order is now predictable](https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/). – p.s.w.g Jun 11 '19 at 18:37
  • No one would do this on a built-in object. That is the purpose of Object literals. Create your own key:value pair data structure rather then mangling up a built-in – Randy Casburn Jun 11 '19 at 18:38
  • does Object.keys have a guaranteed order under all conditions? Is the order based on the order in which the keys are set? – Jordan Klaers Jun 11 '19 at 18:39
  • Yes (depending on how you define "*all*"), and yes. String keys are returned in *insertion* order, e.g. `Object.keys({ foo: 1, bar: 1 })` is guaranteed to return `[ "foo", "bar" ]`. – p.s.w.g Jun 11 '19 at 18:46
  • Note: this guarantee is only true for JS engines that conform to ECMAScript 262 6th edition and on (ES6 and later). String keys are not guaranteed to be returned in insertion order in ES5 and below (which is thankfully barely worth knowing anymore. Unless you get paid to develop for old browsers on intranets etc) – Mike 'Pomax' Kamermans Jun 11 '19 at 18:50

2 Answers2

4

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.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • 3
    Cool this seems to give me the clarification I was looking for; "you can mix them, but dont" – Jordan Klaers Jun 11 '19 at 18:42
  • @JordanKlaers while we're at it, you should also avoid using `for in` in [general](https://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-a-bad-idea). – Zlatko Jun 11 '19 at 18:52
  • 1
    In fact, for arrays there is no good reason to ever use `for` at all; if you need to iterate, use `forEach`. Deriving new data, use `map`. Finding an element, use `find` or `findIndex`, etc. There is no good reason to use a straight up `for` when working with array (and no, "speed optimization" is never a valid excuse: if you really need to eek out speed, `for` is about the last microoptimization you could possibly focus on. Optimise _literally everything else_ first). – Mike 'Pomax' Kamermans Jun 11 '19 at 18:56
  • Furthermore [fill, find, includes, entries, keys, flat, flatMap, join etc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) – Zlatko Jun 11 '19 at 20:40
4

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.

Zlatko
  • 18,936
  • 14
  • 70
  • 123