Array Basics and Core Methods

โ–ถ Try It Yourself

Arrays are the most commonly used data structure in JavaScript โ€” ordered lists of values that can be accessed by index, iterated over, and transformed with a rich set of built-in methods. Every JavaScript developer works with arrays constantly: reading API responses, managing UI lists, processing form data, and building data pipelines. In this lesson you will master array creation, index access, mutation methods, and the full set of methods for adding, removing, and transforming elements.

Array Creation Methods

Method Example Result
Array literal [1, 2, 3] Always use this โ€” clearest syntax
Array.from(iterable) Array.from('hello') ['h','e','l','l','o']
Array.from({length: n}, fn) Array.from({length:3}, (_,i) => i+1) [1,2,3]
Array.of(...items) Array.of(1, 2, 3) [1,2,3]
Spread from Set [...new Set([1,1,2,2])] [1,2] โ€” deduplication

Mutation Methods (change the original array)

Method Effect Returns
push(...items) Add to end New length
pop() Remove last element Removed element
unshift(...items) Add to beginning New length
shift() Remove first element Removed element
splice(start, deleteCount, ...items) Remove/insert at any position Array of removed elements
reverse() Reverse in place The reversed array
fill(value, start, end) Fill range with value The modified array

Non-Mutation Methods (return new array or value)

Method Returns Example
slice(start, end) New array โ€” shallow copy of range [1,2,3,4].slice(1,3) โ†’ [2,3]
concat(...arrays) New merged array [1,2].concat([3,4]) โ†’ [1,2,3,4]
join(sep) String โ€” elements joined by separator ['a','b'].join('-') โ†’ 'a-b'
flat(depth) New flattened array [[1,[2]],3].flat(2) โ†’ [1,2,3]
indexOf(val) First index or -1 [1,2,3].indexOf(2) โ†’ 1
includes(val) Boolean [1,2,3].includes(2) โ†’ true
at(index) Element โ€” supports negative index [1,2,3].at(-1) โ†’ 3
toReversed() ES2023 New reversed array โ€” original untouched Non-mutating alternative to reverse()
toSorted(fn) ES2023 New sorted array โ€” original untouched Non-mutating alternative to sort()
Note: Arrays in JavaScript are objects โ€” typeof [] returns 'object', not 'array'. To correctly check if a value is an array, always use Array.isArray(value). Arrays are zero-indexed: the first element is at index 0 and the last is at arr.length - 1. Accessing an index beyond the array length returns undefined โ€” no out-of-bounds error.
Tip: Prefer non-mutating methods when possible. Methods like slice, concat, and the ES2023 toSorted/toReversed return new arrays without touching the original โ€” making your code more predictable and easier to debug. When you must use a mutating method like sort() or reverse(), copy the array first: [...arr].sort() or arr.slice().sort().
Warning: splice and slice look similar but behave very differently. splice mutates the original array and returns the removed elements. slice does not mutate and returns a new sub-array. Mixing them up is one of the most common JavaScript mistakes. Remember: splice = surgery (cuts the array open); slice = cutting a slice (copy only).

Basic Example

// โ”€โ”€ Creating arrays โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const fruits    = ['apple', 'banana', 'cherry'];
const numbers   = Array.from({ length: 5 }, (_, i) => (i + 1) * 10);  // [10,20,30,40,50]
const chars     = Array.from('hello');           // ['h','e','l','l','o']
const unique    = [...new Set([1, 2, 2, 3, 3])]; // [1,2,3] โ€” deduplication

console.log(numbers);  // [10, 20, 30, 40, 50]
console.log(unique);   // [1, 2, 3]

// โ”€โ”€ Index access โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
console.log(fruits[0]);          // 'apple'
console.log(fruits.at(-1));      // 'cherry' โ€” last element (ES2022)
console.log(fruits[99]);         // undefined โ€” no error

// โ”€โ”€ push / pop โ€” end of array โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const stack = [];
stack.push('a', 'b', 'c');   // add to end
console.log(stack);           // ['a','b','c']
const last = stack.pop();     // remove from end
console.log(last, stack);     // 'c'  ['a','b']

// โ”€โ”€ unshift / shift โ€” beginning of array โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const queue = ['b', 'c'];
queue.unshift('a');          // add to front
console.log(queue);          // ['a','b','c']
const first = queue.shift(); // remove from front
console.log(first, queue);   // 'a'  ['b','c']

// โ”€โ”€ splice โ€” surgical insertion and removal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const letters = ['a', 'b', 'c', 'd', 'e'];

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

// Insert at position 1 without removing
letters.splice(1, 0, 'X', 'Y');
console.log(letters);  // ['a','X','Y','d','e']

// โ”€โ”€ slice โ€” non-mutating copy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const original = [1, 2, 3, 4, 5];
const copy      = original.slice();          // full shallow copy
const middle    = original.slice(1, 4);     // [2,3,4]
const last3     = original.slice(-3);       // [3,4,5]

console.log(original);  // [1,2,3,4,5] โ€” unchanged
console.log(middle);    // [2,3,4]

// โ”€โ”€ concat and spread โ€” merging arrays โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const a = [1, 2, 3];
const b = [4, 5, 6];

const merged1 = a.concat(b);       // [1,2,3,4,5,6]
const merged2 = [...a, ...b];      // [1,2,3,4,5,6] โ€” preferred
const merged3 = [...a, 99, ...b];  // [1,2,3,99,4,5,6] โ€” insert in middle

// โ”€โ”€ flat โ€” flatten nested arrays โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const nested = [[1, 2], [3, [4, 5]]];
console.log(nested.flat());      // [1,2,3,[4,5]] โ€” one level
console.log(nested.flat(2));     // [1,2,3,4,5]   โ€” two levels
console.log(nested.flat(Infinity)); // fully flat โ€” any depth

// โ”€โ”€ includes vs indexOf โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const tags = ['javascript', 'css', 'html'];
console.log(tags.includes('css'));      // true  โ€” boolean check
console.log(tags.indexOf('css'));       // 1     โ€” index or -1
console.log(tags.indexOf('python'));    // -1    โ€” not found

How It Works

Step 1 โ€” Arrays Are Dynamic โ€” No Fixed Size

Unlike arrays in many languages, JavaScript arrays resize automatically. push extends the array; pop shrinks it. You can even set arr.length = 0 to empty an array in place, or arr.length = 10 to pad it with undefined slots. The length property always reflects the current number of elements.

Step 2 โ€” splice Is the Swiss Army Knife

splice(start, deleteCount, ...insertItems) combines deletion and insertion in one operation. Setting deleteCount to 0 inserts without removing. Omitting insertItems removes without replacing. The method modifies the original array in place and returns only the removed elements.

Step 3 โ€” slice Creates a Shallow Copy

slice() with no arguments creates a shallow copy of the entire array โ€” a common technique before calling a mutating method. Shallow means nested objects are not deep-copied โ€” both arrays will share references to the same inner objects. For deep cloning, use structuredClone(arr).

Step 4 โ€” Spread Merges More Expressively Than concat

[...a, ...b] is the modern way to merge arrays. Unlike concat, spread can insert values anywhere in the merge: [...a, 99, ...b]. Spread also works on any iterable โ€” Sets, Maps, strings, NodeLists โ€” while concat only works with arrays.

Step 5 โ€” at() Enables Clean Negative Indexing

arr.at(-1) returns the last element cleanly. Before at(), the idiom was arr[arr.length - 1] โ€” verbose and repeated the array name. at(-2) is the second-to-last, at(-3) the third-to-last, and so on. It works on strings too: 'hello'.at(-1) returns 'o'.

Real-World Example: Shopping Cart

// cart.js

class Cart {
    constructor() {
        this.items = [];
    }

    add(product, qty = 1) {
        const existing = this.items.find(i => i.id === product.id);
        if (existing) {
            existing.qty += qty;
        } else {
            this.items.push({ ...product, qty });
        }
        return this;   // enable chaining
    }

    remove(productId) {
        const idx = this.items.findIndex(i => i.id === productId);
        if (idx !== -1) this.items.splice(idx, 1);
        return this;
    }

    updateQty(productId, qty) {
        if (qty <= 0) return this.remove(productId);
        const item = this.items.find(i => i.id === productId);
        if (item) item.qty = qty;
        return this;
    }

    get total()     { return this.items.reduce((sum, i) => sum + i.price * i.qty, 0); }
    get itemCount() { return this.items.reduce((sum, i) => sum + i.qty, 0); }
    get isEmpty()   { return this.items.length === 0; }

    getNames()  { return this.items.map(i => `${i.name} x${i.qty}`); }
    clear()     { this.items = []; return this; }

    summary() {
        return {
            items:    this.getNames(),
            count:    this.itemCount,
            total:    `$${this.total.toFixed(2)}`,
            shipping: this.total >= 35 ? 'Free' : '$5.99',
        };
    }
}

const cart = new Cart();
cart
    .add({ id: 1, name: 'Widget',   price: 9.99  })
    .add({ id: 2, name: 'Gadget',   price: 29.99 })
    .add({ id: 1, name: 'Widget',   price: 9.99  }, 2);  // add 2 more Widgets

console.log(cart.summary());
// { items: ['Widget x3','Gadget x1'], count: 4, total: '$59.96', shipping: 'Free' }

cart.updateQty(1, 1);
cart.remove(2);
console.log(cart.summary());
// { items: ['Widget x1'], count: 1, total: '$9.99', shipping: '$5.99' }

Common Mistakes

Mistake 1 โ€” Confusing splice and slice

โŒ Wrong โ€” splice mutates the array unexpectedly:

const arr = [1, 2, 3, 4, 5];
const sub = arr.splice(1, 3);   // removes [2,3,4] from arr!
console.log(arr);               // [1, 5] โ€” mutated!

โœ… Correct โ€” use slice to copy a range without mutation:

const arr = [1, 2, 3, 4, 5];
const sub = arr.slice(1, 4);    // [2,3,4] โ€” arr unchanged
console.log(arr);               // [1,2,3,4,5]

Mistake 2 โ€” Sorting an array you still need unsorted

โŒ Wrong โ€” sort() mutates the original:

const prices = [30, 10, 50, 20];
const sorted = prices.sort((a, b) => a - b);
console.log(prices);   // [10,20,30,50] โ€” original mutated!

โœ… Correct โ€” copy before sorting:

const sorted = [...prices].sort((a, b) => a - b);
console.log(prices);   // [30,10,50,20] โ€” original preserved

Mistake 3 โ€” Using delete to remove an array element

โŒ Wrong โ€” delete leaves an empty slot (undefined) and doesn’t change length:

const arr = [1, 2, 3];
delete arr[1];
console.log(arr);        // [1, empty, 3] โ€” length still 3!

โœ… Correct โ€” use splice to actually remove the element:

arr.splice(1, 1);
console.log(arr);        // [1, 3] โ€” length is now 2

▶ Try It Yourself

Quick Reference

Task Method Mutates?
Add to end push(item) Yes
Remove from end pop() Yes
Add to start unshift(item) Yes
Remove from start shift() Yes
Remove/insert anywhere splice(i, n, ...items) Yes
Copy range slice(start, end) No
Merge arrays [...a, ...b] No
Flatten flat(depth) No
Check membership includes(val) No
Last element at(-1) No

🧠 Test Yourself

Which method removes the last element from an array AND returns it?





โ–ถ Try It Yourself