Arrays Basics

Difficulty: Beginner

Arrays are ordered collections of values and one of the most frequently used data structures in JavaScript. Unlike some languages, JavaScript arrays are dynamic - they can grow and shrink at runtime, hold values of any type (including mixed types in the same array), and provide a rich set of built-in methods for manipulation. Arrays are zero-indexed, meaning the first element is at index 0.

You create arrays using array literal syntax (square brackets) or the Array constructor. The literal syntax is strongly preferred: const fruits = ["apple", "banana", "cherry"]. Access elements using bracket notation with the index: fruits[0] is "apple". The length property tells you how many elements the array contains. You can also set the length property to truncate an array or create sparse arrays, though the latter is generally discouraged.

JavaScript arrays provide four core methods for adding and removing elements from either end. push() adds elements to the end and returns the new length. pop() removes and returns the last element. unshift() adds elements to the beginning and returns the new length. shift() removes and returns the first element. Push and pop operate on the end of the array and are O(1) operations, while unshift and shift operate on the beginning and require re-indexing all elements, making them O(n).

The splice() method is the Swiss Army knife of array mutation - it can insert, remove, and replace elements at any position. Its signature is array.splice(start, deleteCount, ...items). It modifies the original array and returns an array of deleted elements. In contrast, slice() is non-mutating - it returns a shallow copy of a portion of an array without changing the original. Other useful methods include concat() (merges arrays without mutation), indexOf() and includes() for searching, and Array.isArray() for type checking.

Array destructuring, introduced in ES6, provides elegant syntax for extracting values from arrays into individual variables. The syntax mirrors array literal notation: const [first, second, third] = myArray. You can skip elements with empty commas, use rest syntax to collect remaining elements, and provide default values. Destructuring is widely used in modern JavaScript, especially for function return values and React hooks.

Multidimensional arrays are simply arrays that contain other arrays as elements. A 2D array represents a grid or matrix: const grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]. Access elements with chained bracket notation: grid[1][2] is 6 (row 1, column 2). While JavaScript does not have native matrix operations, nested arrays are commonly used for game boards, spreadsheet data, and mathematical computations.

Code examples

Creating and Accessing Arrays

// Array literal (preferred)
const fruits = ["apple", "banana", "cherry"];
console.log(fruits[0]);       // "apple"
console.log(fruits[2]);       // "cherry"
console.log(fruits.length);   // 3
console.log(fruits[fruits.length - 1]); // "cherry" (last element)

// Arrays can hold mixed types
const mixed = [42, "hello", true, null, { key: "value" }, [1, 2]];
console.log(mixed.length); // 6

// Array.isArray - reliable type check
console.log(Array.isArray(fruits));   // true
console.log(Array.isArray("hello"));  // false
console.log(typeof fruits);           // "object" (not helpful!)

// Array.from - create arrays from iterables
console.log(Array.from("hello"));          // ["h","e","l","l","o"]
console.log(Array.from({ length: 5 }, (_, i) => i)); // [0,1,2,3,4]

Arrays are zero-indexed. Use array[array.length - 1] to access the last element (or array.at(-1) in ES2022+). typeof returns 'object' for arrays, so always use Array.isArray() for type checking. Array.from() creates arrays from iterables or array-like objects.

push, pop, shift, unshift

const stack = ["a", "b", "c"];

// push - add to end
const newLength = stack.push("d", "e");
console.log(stack);      // ["a","b","c","d","e"]
console.log(newLength);  // 5

// pop - remove from end
const last = stack.pop();
console.log(last);       // "e"
console.log(stack);      // ["a","b","c","d"]

// unshift - add to beginning
stack.unshift("z");
console.log(stack);      // ["z","a","b","c","d"]

// shift - remove from beginning
const first = stack.shift();
console.log(first);      // "z"
console.log(stack);      // ["a","b","c","d"]

// Stack behavior (LIFO) using push/pop
const callStack = [];
callStack.push("main()");
callStack.push("render()");
callStack.push("update()");
console.log(callStack.pop()); // "update()"  (last in, first out)
console.log(callStack.pop()); // "render()"
console.log(callStack);       // ["main()"]

push/pop work on the end (fast, O(1)). shift/unshift work on the beginning (slower, O(n) because all elements must be re-indexed). Push/pop together implement a stack (last-in, first-out). Push/shift together implement a queue (first-in, first-out).

splice (mutating) vs slice (non-mutating)

// splice(start, deleteCount, ...items) - MUTATES the array
const letters = ["a", "b", "c", "d", "e"];

// Remove 2 elements starting at index 1
const removed = letters.splice(1, 2);
console.log(removed);    // ["b", "c"]
console.log(letters);    // ["a", "d", "e"]

// Insert without removing (deleteCount = 0)
letters.splice(1, 0, "x", "y");
console.log(letters);    // ["a", "x", "y", "d", "e"]

// Replace elements
letters.splice(1, 2, "B", "C");
console.log(letters);    // ["a", "B", "C", "d", "e"]

// slice(start, end) - does NOT mutate
const nums = [10, 20, 30, 40, 50];
console.log(nums.slice(1, 3));   // [20, 30]
console.log(nums.slice(2));      // [30, 40, 50]
console.log(nums.slice(-2));     // [40, 50]
console.log(nums);               // [10, 20, 30, 40, 50] (unchanged)

// Shallow copy with slice
const copy = nums.slice();
copy.push(60);
console.log(nums);  // [10, 20, 30, 40, 50] (original unchanged)
console.log(copy);  // [10, 20, 30, 40, 50, 60]

splice MUTATES the original array - use it for in-place modifications. slice returns a NEW array - use it for extracting portions or making copies. Remember: splice = mutating (has a 'p' for 'permanent change'), slice = non-mutating (has no 'p').

Destructuring and Multidimensional Arrays

// Array destructuring
const rgb = [255, 128, 0];
const [red, green, blue] = rgb;
console.log(`R: ${red}, G: ${green}, B: ${blue}`);

// Skipping elements
const [first, , third] = ["a", "b", "c", "d"];
console.log(first, third); // "a" "c"

// Rest pattern in destructuring
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]

// Default values
const [x = 0, y = 0, z = 0] = [10, 20];
console.log(x, y, z); // 10 20 0

// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1

// Multidimensional array
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
console.log(matrix[1][2]); // 6 (row 1, col 2)

// Iterating a 2D array
for (const row of matrix) {
  console.log(row.join(" "));
}

Destructuring extracts array values into named variables using positional matching. The rest pattern (...tail) collects remaining elements. Variable swapping with [a, b] = [b, a] is an elegant ES6 pattern. Multidimensional arrays use chained bracket notation for access.

Key points

Concepts covered

Array Creation, Array Methods, push/pop/shift/unshift, splice/slice, Array Destructuring, Multidimensional Arrays