JavaScript · Beginner

JavaScript Arrays: Creating, Indexing and Changing Lists

10 min readUpdated September 24, 2026Every example verified

In short: An array is an ordered list of values, indexed from 0, that can grow and shrink. You read an element with [i], count them with .length, add and remove with push, pop, shift and unshift, copy a range with slice, and visit elements with a loop or with methods such as map, filter, forEach and reduce.

Ordered lists of values

An array holds any number of values in order. It is written as a comma-separated list in square brackets, and the values can be of any type, including other arrays and objects, though most arrays hold one kind of thing. The first element is at index 0 and the last at length - 1; reading an index that does not exist gives undefined rather than an error, which is convenient and also a common source of silent bugs.

Arrays are mutable. push adds to the end and pop removes from the end; unshift and shift do the same at the front. splice(start, count, ...items) removes count elements at start and optionally inserts new ones; it is the general-purpose editing tool. Assigning arr[i] = value replaces one element, and assigning to length truncates. The non-mutating counterparts return a new array and leave the original alone: slice(start, end) copies a range (with end excluded and negative indexes counting from the back), concat joins arrays, and map and filter build new arrays from old.

Searching is done with indexOf (the position, or -1), includes (a boolean), find (the first element satisfying a test) and findIndex. join(separator) turns the elements into one string, and at(-1) (ES2022) is a readable way to fetch the last element.

The method most people misuse is sort. With no argument it converts every element to a string and sorts those, so numbers come out in dictionary order: 100 before 5. Pass a comparison function, (a, b) => a - b for ascending numbers, and it sorts numerically. sort and reverse also change the array in place; copy first with slice() if you need the original order too. ES2023 added toSorted() and toReversed(), which return sorted copies, but sorting a slice() works everywhere.

Two facts about arrays as values matter from the start. First, a variable holds a reference to the array, not the array itself: const b = a makes two names for one array, and pushing through either shows up in both. To copy, use slice() or the spread syntax from the spread lesson. Second, === compares references, so two separately built arrays with identical contents are not equal.

The iteration methods take a function and call it for each element: forEach for side effects, map to transform, filter to select, reduce to fold everything into one value, some and every to test. They read as a pipeline and avoid index arithmetic; a plain for...of loop from the loops lesson is equally fine when you need to stop early.

Syntax

 JavaScript · syntax
const list = [10, 20, 30];        // literal
list[0]        list.length       list.at(-1)      // read; last element
list.push(40)  list.pop()        list.unshift(5)  list.shift()   // ends
list.splice(1, 2)                 // remove 2 elements at index 1 (in place)
list.slice(1, 3)                  // copy indexes 1 and 2 (original untouched)
list.indexOf(20)  list.includes(20)  list.find((x) => x > 15)
list.join(", ")                   // "10, 20, 30"
list.sort((a, b) => a - b)        // numeric ascending, in place
list.map((x) => x * 2)  list.filter((x) => x > 10)  list.reduce((acc, x) => acc + x, 0)
list.forEach((x, i) => { })       // value and index, no result

Methods that end in a new array (slice, map, filter, concat) leave the original alone; push, pop, shift, unshift, splice, sort and reverse change it in place.

Mutating versus non-mutating methods

Changes the array in placeReturns something new, array untouched
push, pop, shift, unshiftslice, concat
splicemap, filter, reduce
sort, reversejoin, indexOf, includes, find
arr[i] = value, arr.length = ntoSorted, toReversed (ES2023)

A waiting queue at a clinic

People join at the back, the front is served, and one urgent case is pushed in at the front.

 JavaScript
const queue = ["Ines", "Farid"];
queue.push("Lena");
console.log(queue);
console.log("Waiting:", queue.length);
console.log("First:", queue[0], "Last:", queue[queue.length - 1]);
console.log("Also last:", queue.at(-1));

const served = queue.shift();
console.log("Served:", served);
console.log(queue);

queue.unshift("Otto (urgent)");
console.log(queue);
console.log(queue.indexOf("Lena"), queue.includes("Farid"), queue.includes("Ines"));
console.log(queue[10]);

Output

[ 'Ines', 'Farid', 'Lena' ]
Waiting: 3
First: Ines Last: Lena
Also last: Lena
Served: Ines
[ 'Farid', 'Lena' ]
[ 'Otto (urgent)', 'Farid', 'Lena' ]
2 true false
undefined

push and shift together make the array behave as a queue: join at the back, leave from the front. shift returns the element it removed, so the served name can be printed. After the urgent case is inserted at the front, Lena has moved to index 2, which indexOf reports. Reading index 10 of a three-element array quietly gives undefined; a program that then does arithmetic with it gets NaN and no error message, so check length before indexing.

References, copies, slice and splice

Two names share one array, a slice makes an independent copy, and splice edits in place.

 JavaScript
const scores = [40, 75, 60, 90];
const alias = scores;
alias.push(55);
console.log(scores);

const copy = scores.slice();
copy.push(100);
console.log(scores.length, copy.length);
console.log(scores.slice(1, 3));
console.log(scores.slice(-2));

const removed = scores.splice(1, 2);
console.log("Removed:", removed);
console.log("Now:", scores);
scores.splice(1, 0, 61, 62);
console.log(scores);
console.log(scores.join(" | "));
console.log(scores === alias, scores === copy);

Output

[ 40, 75, 60, 90, 55 ]
5 6
[ 75, 60 ]
[ 90, 55 ]
Removed: [ 75, 60 ]
Now: [ 40, 90, 55 ]
[ 40, 61, 62, 90, 55 ]
40 | 61 | 62 | 90 | 55
true false

alias is not a second array; it is a second name for the same one, so the push through alias appears when scores is printed. slice() with no arguments copies the whole array, and the copy grows without affecting the original. slice(1, 3) takes indexes 1 and 2 (the end is excluded) and slice(-2) takes the last two. splice(1, 2) removes two elements starting at index 1 and returns them; splice(1, 0, 61, 62) removes nothing and inserts two values. The last line confirms that === is about identity: alias is the same array, copy is a different one.

Sorting numbers and the iteration methods

Compare the two sort results, then watch map, filter, reduce, forEach, find, some and every.

 JavaScript
const prices = [12, 5, 100, 30];
console.log(prices.slice().sort());
console.log(prices.slice().sort((a, b) => a - b));
console.log(prices);

const doubled = prices.map((p) => p * 2);
const cheap = prices.filter((p) => p < 20);
const total = prices.reduce((sum, p) => sum + p, 0);
console.log(doubled);
console.log(cheap);
console.log(total);

prices.forEach((p, i) => console.log(i + ": " + p));
console.log(prices.find((p) => p > 20), prices.some((p) => p > 50), prices.every((p) => p > 50));

Output

[ 100, 12, 30, 5 ]
[ 5, 12, 30, 100 ]
[ 12, 5, 100, 30 ]
[ 24, 10, 200, 60 ]
[ 12, 5 ]
147
0: 12
1: 5
2: 100
3: 30
100 true false

The default sort compares "100", "12", "30" and "5" as text, and "1" sorts before "5", which is why 100 comes first. The comparison function (a, b) => a - b returns a negative number when a should come first, and the sort becomes numeric. Sorting a slice() leaves prices in its original order, as the third line shows. map builds a same-length array of results, filter keeps the elements whose test is true, and reduce threads an accumulator through every element starting from 0. find returns the first match (100), some asks whether any element passes and every whether all do.

Common mistakes

  • Sorting numbers with a bare sort()

    Why it goes wrong: Without a comparison function the elements are compared as strings, so [10, 9, 1].sort() gives [1, 10, 9].

    Fix: Pass a comparator: (a, b) => a - b for ascending, (a, b) => b - a for descending.

     JavaScript · fix
    const n = [10, 9, 1];
    n.sort((a, b) => a - b);
    console.log(n); // [ 1, 9, 10 ]
  • Expecting assignment to copy an array

    Why it goes wrong: const backup = items; creates a second reference to the same array, so changes through either name affect both, and the "backup" is nothing of the kind.

    Fix: Copy with items.slice() (or [...items]), remembering that objects inside the array are still shared.

  • Using the return value of push as the array

    Why it goes wrong: push returns the new length, not the array, so const list = [].push(1); leaves list holding the number 1.

    Fix: Call push on an existing array as a statement, then use the array itself.

     JavaScript · fix
    const list = [];
    list.push(1);
    console.log(list); // [ 1 ]
  • Comparing two arrays with ===

    Why it goes wrong: [1, 2] === [1, 2] is false because the operator compares identity, not contents.

    Fix: Compare lengths and then each element in a loop, or for arrays of simple values compare a.join(",") === b.join(",").

Where you use this

Most exercises read a list of values and then ask a question about it: the total, the largest, how many pass a test, the values in order. The natural approach is to read every line into an array first and then answer with the array methods, which keeps input handling and logic apart. Sorting a copy and reading at(0) and at(-1) gives the smallest and largest; filter(...).length counts matches; reduce totals. For a report, map formats each element and join glues the lines together for a single console.log.

 JavaScript · in practice
const values = [];
let line;
while ((line = readline()) !== null) {
  values.push(Number(line));
}
const sorted = values.slice().sort((a, b) => a - b);
console.log("min", sorted.at(0), "max", sorted.at(-1));
console.log("above 10:", values.filter((v) => v > 10).length);

Key points

  • Indexes start at 0 and end at length - 1; a missing index reads as undefined.
  • push/pop work at the end, unshift/shift at the front, splice anywhere, all in place.
  • slice copies a range and leaves the original alone; splice edits the original.
  • sort() compares as strings; pass (a, b) => a - b for numbers, and sort a copy if order matters.
  • const b = a shares one array; === compares identity, not contents.
  • map, filter, reduce, forEach, find, some, every take a function and apply it to each element.

Try it yourself

The starter collects every input line into an array. Print the number of items, then the items in reverse order on one line separated by a comma and a space.

Your program
const items = [];
let line;
while ((line = readline()) !== null) {
  items.push(line);
}
// print the count, then the items reversed and joined with ", "
Input the program receives: pen ↵ ink ↵ paper
Expected output: 3 paper, ink, pen

Practise this

Open the JavaScript playground

Frequently asked questions

How do I copy an array in JavaScript?

original.slice() or [...original] both create a new array with the same elements; Array.from(original) does too. All three are shallow copies: if the elements are objects, the copy holds the same objects, so changing one of them is visible through both arrays. For a deep copy of plain data, structuredClone(original) (Node.js 17 and later, and current browsers) copies nested arrays and objects as well.

Why does sort() put 100 before 5?

The default comparison converts elements to strings and compares them character by character, and the character "1" comes before "5". To sort numerically, pass a comparison function that returns a negative number, zero or a positive number: arr.sort((a, b) => a - b).

What is the difference between slice and splice?

slice(start, end) returns a new array containing a copy of the elements from start up to but not including end, and does not change the original. splice(start, count, ...items) removes count elements at start from the original array, inserts any items you pass, and returns the removed elements. One reads, the other edits.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with Node.js 22 at build time; runs in your browser in an isolated Web Worker by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.