# Arrays are objects in JavaScript

# Arrays are objects

First, let's confirm that arrays are objects.

This is key to understanding how they work:

```javascript
const arr = ['a', 'b', 'c']

// Under the hood, it's like this:
const arrAsObject = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}

// We can prove this
typeof []  // "object"
[] instanceof Object  // true
```

# Arrays special behaviors

Arrays have some special behaviors that regular objects don't:

```javascript
// 1. Length is automatically managed
const arr = ["a"];
arr[1] = "b";
console.log(arr.length); // 2

// 2. Indices are converted to strings
arr["2"] = "c"; // Same as arr[2] = 'c'
console.log(arr); // ['a', 'b', 'c']

// 3. You can add non-index properties
arr.customProp = "hello";
console.log(arr.customProp); // "hello"
console.log(arr.length); // Still 3 (properties don't count)
```

# Array length

The length property is special:

```javascript
const arr = ["a", "b", "c"];
arr.length = 1; // Truncates the array!
console.log(arr); // ['a']

arr.length = 3; // Extends with empty slots
console.log(arr); // ['a', empty × 2]
```

# Sparse arrays

Sparse arrays (arrays with holes) are possible because of this object nature:

```javascript
const sparse = [];
sparse[0] = "first";
sparse[2] = "third";
console.log(sparse); // ['first', empty, 'third']
console.log(sparse.length); // 3

// Empty slots are different from undefined
sparse[1] = undefined;
console.log(0 in sparse); // true
console.log(1 in sparse); // true
console.log(3 in sparse); // false
```

# Prototype

Array methods exist on Array.prototype:

```javascript
const arr = ["a", "b"];
console.log(arr.__proto__ === Array.prototype); // true
console.log(arr.__proto__.__proto__ === Object.prototype); // true
```

This object-like nature affects performance:

```javascript
// V8 can optimize sequential, numeric properties
const optimized = ["a", "b", "c"];

// But mixing types or adding non-index properties can deoptimize
optimized.prop = "bad";
optimized[0.5] = "worse"; // Non-integer index
```

If you wanna learn more about JS internals, read: [How to write performant JavaScript code](https://tigerabrodi.blog/how-to-write-performant-javascript-code).

And finally, this is why array-like objects exist.

They mimic this structure:

```javascript
const arrayLike = {
  0: "a",
  1: "b",
  length: 2,
};

// We can convert it to a real array
const realArray = Array.from(arrayLike);
// Or
const alsoReal = [...arrayLike]; // If it's iterable
```

This object nature of arrays explains many JavaScript behaviors:

* Why we can add properties to arrays
    
* Why array lengths can be manipulated
    
* Why sparse arrays are possible
    
* Why array-like objects work
    
* How prototype inheritance works for arrays
    

The JavaScript engine optimizes common array patterns, but knowing that arrays are objects helps us understand their flexibility and limits.

# More on the JS engine

```javascript
// V8 has different ways it stores arrays internally based on how we use them

// 1. The fastest: packed elements
const packed = [1, 2, 3]  // Sequential, same type (all numbers)
// V8 can store this in continuous memory blocks
// Direct memory access, very fast

// 2. Still fast: holey elements
const holey = [1, , 3]    // Has a hole
// V8 needs extra checks when accessing elements
// Must check if element exists

// 3. Slower: mixed elements
const mixed = [1, "string", 3]  // Different types
// V8 can't optimize as well
// Must handle different types

// Even slower patterns:
const bad1 = []
bad1[1000] = 1  // Creates sparse array
// V8 switches to dictionary mode (hash table)

const bad2 = [1, 2, 3]
bad2.foo = 'bar'  // Non-index property
// Can force V8 to switch to a slower mode
```

V8 has specific array modes:

* `PACKED_SMI`: All small integers
    
* `PACKED_DOUBLE`: All doubles
    
* `PACKED_ELEMENTS`: All same type
    
* `HOLEY_SMI`: Small integers with holes
    
* `HOLEY_ELEMENTS`: Mixed with holes
    

Every time we do something that doesn't match the current mode, V8 has to switch to a more general (slower) mode. It can't switch back to a faster mode.

```javascript
const arr = [1, 2, 3]  // Starts as PACKED_SMI
arr.push(4.5)          // Transitions to PACKED_DOUBLE
arr.push("string")     // Transitions to PACKED_ELEMENTS
delete arr[1]          // Transitions to HOLEY_ELEMENTS
```

This is why performance-critical code often:

* Sets the array size in advance
    
* Keeps arrays full (no holes)
    
* Uses the same types
    
* Avoids adding properties that aren't indexed
