Arrays in javascript
Arrays in JavaScript
Deep dive into memory layout, mutation vs immutability, and essential built-in operations.
The Swiss Army Train of Compartments
JavaScript arrays are special list-like objects whose prototype is `Array.prototype`. Under the hood in engines like V8, dense packed arrays are stored as contiguous memory for speed, while sparse arrays transition to dictionary mode.
V8 Array Elements Kinds
V8 tracks internal element kinds: `PACKED_SMI_ELEMENTS` (small integers), `PACKED_DOUBLE_ELEMENTS` (floats), and `PACKED_ELEMENTS` (mixed objects/strings). Adding holes creates `HOLEY` variants that are slower.
Mutating Methods (Change in Place)
`push()`, `pop()`, `shift()`, `unshift()`, `splice()`, `reverse()`, and `sort()`. These alter the original array in memory.
Non-Mutating Methods (Return New Array)
`concat()`, `slice()`, `map()`, `filter()`, `flat()`, and modern ES2023 additions: `toSorted()`, `toReversed()`, and `toSpliced()`.
Higher-Order Iterators
`forEach()`, `map()`, `filter()`, `reduce()`, `some()`, `every()`, `find()`, and `findIndex()`. These accept callbacks and improve readability over imperative for-loops.
Comparing in-place mutation vs modern non-mutating transformations:
const scores = [45, 92, 18, 77];
// Mutating sort (converts elements to strings by default if comparator omitted!)
const mutated = [...scores].sort((a, b) => a - b);
console.log("Sorted:", mutated);
// Non-mutating pipeline
const passingBonus = scores
.filter(score => score >= 50)
.map(score => score + 5);
console.log("Passing with bonus:", passingBonus);
console.log("Original scores untouched:", scores);
Sorted: [ 18, 45, 77, 92 ]
Passing with bonus: [ 97, 82 ]
Original scores untouched: [ 45, 92, 18, 77 ]
Default sort() Lexicographical Trap
The Risk: `[10, 2, 25, 5].sort()` produces `[10, 2, 25, 5]` because it stringifies elements before comparing (`"10"` comes before `"2"`).
The Fix: Always supply an explicit numeric comparator: `arr.sort((a, b) => a - b)` for ascending order.
Confusing splice() with slice()
The Risk: `arr.splice(start, count)` modifies the array in-place, whereas `arr.slice(start, end)` returns a shallow copy without touching original.
The Fix: Remember: **sp**lice **sp**lits the original array; **sl**ice takes a **sl**ice copy.
- ✓ Arrays are reference types stored in the Heap.
- ✓ Use `slice()`, `concat()`, or spread `[...arr]` for immutability in React/Redux.
- ✓ Always pass a comparator to `.sort((a, b) => a - b)`.
- ✓ Use `find()` / `findIndex()` to locate single items; `filter()` for all matches.
- ✓ Avoid creating sparse arrays with holes (e.g. `arr[1000] = "x"`) to keep V8 optimizations.