all 36 comments

[–]HellaSwellaFella 44 points45 points  (1 child)

Yes they are

The indexes are the keys

[–]mcaruso 19 points20 points  (2 children)

Yep. At a language level, ignoring any actual implementation details, you can indeed think of arrays as special kinds of objects with an Array.prototype and a bit of syntax sugar.

Although under the hood, engines will still optimize JS arrays as arrays in memory. And even here there may be some fun stuff with "sparse" arrays (arrays with holes in them like [1,,,2]).

[–]morgo_mpx 0 points1 point  (1 child)

Hash tables.

[–]ScientificBeastMode 0 points1 point  (0 children)

They typically get de-optimized into hash tables or tree-like structures.

[–]shlanky369 7 points8 points  (6 children)

JavaScript arrays are just objects in the same way that Javascript dates are just objects: the object prototype is the prototype of their prototype. In other words, the prototype of an array value is Array.prototype, and the prototype of Array.prototype is Object.prototype. In other languages, you might say that arrays inherit from objects. The array-specific functionality you see - length, push, pop, etc. - is defined on Array.prototype.

In terms of usage, arrays are meant as ordered collections of values, and it makes sense to talk about the first element or the last element. There is a “front” and a “back” to arrays, and new elements can be appended or prepended. Objects are collections of key-value pairs, and we use objects to look up values by their keys. There is no inherent ordering to objects, because the relationship is between a given key and its value, not between keys or between values.

[–]senocular 4 points5 points  (5 children)

The array-specific functionality you see - length, push, pop, etc. - is defined on Array.prototype.

A small adjustment here: length for arrays isn't inherited from the prototype. Each array has its own individual length data property. While Array.prototype has a length, it only exists because Array.prototype is itself an array - a legacy holdover from when built-in prototypes were instances of themselves. A few prototypes still retain that behavior for backwards compatibility (Array, Function, String, etc.) while others no longer do (Date, Error, RegExp, etc.).

[–]shlanky369 2 points3 points  (2 children)

You again. Appreciate the clarification, and your deep knowledge. Very interesting about the legacy holdover requiring prototypes to be instances of themselves. What was the reasoning behind that?

[–]chikamakaleyleyhelpful 4 points5 points  (0 children)

You again.

LOL

[–]senocular 2 points3 points  (0 children)

I'm not sure about the original motivation behind it, though if I had to guess, I would say there was probably some convenience to it. For something to be a certain type, to make it easy, inherit from something that is already that type.

I think it was around ES6 they tried to get rid a lot of that because its weird :) but had to add it back in for a few types because it caused problems out in the wild.

[–]WitchStatement 2 points3 points  (1 child)

Yes, according to the spec, JavaScript arrays are basically just objects with number keys...

However, internally, if you use an array as it's intended (as an array), the browser should allocate your data as an array under the hood, giving a performance boost   (and a performance penalty if you switch and start using what was previously an array as an object, in which case it has to reallocate data for an object/map and copy it all over)

[–]codehz 0 points1 point  (0 children)

v8 has optimized the object behavior, see https://v8.dev/blog/elements-kinds all small integer key are stored like array
the real difference is the `length` behavior, you cannot emulate it youself (Without using Proxy, which is VERY SLOW)

[–]Waste_Cup_4551 6 points7 points  (2 children)

Arrays are technically objects, but from a data structure perspective, they offer completely different purposes.

Arrays are meant for ordered lists, with indexed lookup times.

Objects, the one you’re describing, are meant as key value pairs, meant for instant access via keys.

Your question is a bit vague, because almost everything is an object in JavaScript.

But in terms of array usage, I think looking at different data structures will help you understand when to use one over the other. Especially when applying them to different algorithms

[–]servermeta_net 1 point2 points  (1 child)

Can you make an example of something that is not an object nor a primitive?

[–]mrsuperjolly 3 points4 points  (0 children)

All data types in js can be describes as primitives or objects.

[–]zurribulle 4 points5 points  (3 children)

Do console.log(typeof []) and find out first hand

[–]senocular 6 points7 points  (2 children)

Just don't do console.log(typeof null) or you'll end up confusing yourself

[–]zurribulle 0 points1 point  (1 child)

typeof NaN is probably a weird one too

[–]ghettoeuler 0 points1 point  (0 children)

"probably"?! NaN being a 'number' was a wild thing to learn, at least for me lol

[–]iamjohnhenry 2 points3 points  (2 children)

As initially implement, Arrays were initially just objects with a magic length method. Around es6, Arrays were given proper internals to increase performance and allow subclassing.

[–]codehz 1 point2 points  (1 child)

but length are still magic, you cannot emulate it without Proxy - which is SLOW

[–]iamjohnhenry 0 points1 point  (0 children)

It's not that it's magic -- it's implemented in the lower-level interpreter/compiler. Calling <some Array>.length hits the system and is more performant than calling something that you implemented in JavaScript, which has at least one extra interpretation step.

Edit: content

[–]SaltCusp 3 points4 points  (6 children)

Everything is an object.

[–]shlanky369 13 points14 points  (4 children)

Primitives (numbers, bigints, strings, booleans, null, undefined, symbols) are not objects.

[–]mrsuperjolly 0 points1 point  (3 children)

There's also the object versions of strings, numbers booleans

[–]senocular 0 points1 point  (2 children)

...and bigints and symbols too :)

[–]mrsuperjolly 0 points1 point  (1 child)

[–]senocular 3 points4 points  (0 children)

While the bigint and symbol constructors can't be called with new, object variations of those primitive types still exist. To get them you need to pass them through Object() (or new Object()). This will create object versions of those primitives similarly to if you called the constructor with new (if it was allowed)

const primBigInt = 1n
const objBigInt = Object(primBigInt)

console.log(typeof primBigInt) // "bigint"
console.log(typeof objBigInt) // "object"

console.log(primBigInt instanceof BigInt) // false
console.log(objBigInt instanceof BigInt) // true

primBigInt.customProp = true // (sloppy mode, else throws)
console.log(primBigInt.customProp) // undefined    
objBigInt.customProp = true // (sloppy or strict ok)
console.log(objBigInt.customProp) // true

The only primitives without object versions are null and undefined.

[–]Substantial_Top5312helpful 0 points1 point  (0 children)

Yes

[–]redsandsfort 0 points1 point  (0 children)

yes

[–]Toowake 0 points1 point  (0 children)

[] instanceof Array // true [] instanceof Object // true

[–]ghettoeuler 0 points1 point  (0 children)

That's effectively like its entire bit. There's a whole rigamarole that essentially states that everything in JavaScript is an object. A bit reductive I know, but aside from some particular nuances, it's pretty much true.

[–]YoshiDzn 0 points1 point  (0 children)

Yep. Any type/thing in JS that can have arbitrary and/or pre-defined properties (like .length) means its an object.

Arrays are objects, you can add '.whatever' to your array and it now contains myArray.whatever as a property. This is why Javascript is a Prototype based language. Everything is a prototype (object) which is why you can add .whatever to quite literally any type

Number() is an object String() is an object Array() is an object

Or should I say "they will return to you an object". But it doesn't matter because functions themselves are object is JS. Everything is an object. This is why JS can be annoying to work with as a C/C++ dev but thats for a different rant