Key Takeaways
- Arrays store ordered collections and use zero-based indexes: the first element is at index 0.
- Prefer array literals such as [4]. The constructor new Array(4) creates four empty slots, not an array containing 4.
- Use map() to transform values, filter() to select values, find() to locate one match, and reduce() to accumulate a result.
- sort() and splice() change the original array. Modern alternatives such as toSorted() and toSpliced() return new arrays.
JavaScript arrays are ordered, indexed collections. JavaScript array methods let you iterate, transform, search, combine, and edit those collections without writing every operation as a loop. Arrays can contain numbers, strings, objects, other arrays, or a mixture of these values.
Despite the title, this guide isn't limited to ES6, another name for ES2015. It also covers includes() from ES2016, flat() and flatMap() from ES2019, at() from ES2022, and copying and reverse-search methods introduced in ES2023. Run each code block independently in a browser or JavaScript runtime that supports the methods shown; check the linked compatibility tables when supporting older environments.
Defining Arrays
Use an array literal for ordinary array creation. The constructor also accepts multiple elements, but the literal makes your intent clearer.
const scores = [87, 43, 88, 99];
const empty = [];
const constructedScores = new Array(87, 43, 88, 99);
console.log(scores); // [87, 43, 88, 99]
console.log(empty); // []
console.log(constructedScores); // [87, 43, 88, 99]JavaScript is dynamically typed: values have types, but a variable isn't restricted to one declared type. An array can therefore contain different types; calling it “untyped” is misleading.
Choose arrays when position and sequence matter. Use a Set for unique values, or a Map or object for key-based lookup rather than positional access. Our JavaScript objects tutorial covers the object alternative.
The Array Constructor and Empty Slots
A single numeric constructor argument sets the array's length instead of adding that number as an element. The resulting empty slots are missing elements, not stored undefined values.
const slots = new Array(4);
const value = [4];
console.log(slots.length); // 4
console.log(value.length); // 1
console.log(0 in slots); // false: index 0 is missing
console.log(0 in value); // true: index 0 contains 4An array with missing elements is called a sparse array. Its length isn't necessarily the number of populated elements. Methods including forEach(), map(), and filter() skip empty slots; an explicitly stored undefined is still an element and receives a callback.
Creating Arrays with Array.from()
Array.from() creates an array from an iterable—a value you can consume in sequence—or an array-like object with indexed properties and a length. Its optional mapping callback is also useful for creating populated sequences.
const nameArray = Array.from("Sam");
const indexes = Array.from({ length: 4 }, (_, index) => index);
console.log(nameArray); // ["S", "a", "m"]
console.log(indexes); // [0, 1, 2, 3]For related text operations, see JavaScript string methods.
Accessing Array Elements
Arrays use zero-based indexing. names[0] retrieves the first element, “Steve”, and names[2] retrieves the third element, “Sam”.
const names = ["Steve", "Bob", "Sam", "Erick"];
const firstName = names[0];
const thirdName = names[2];
console.log(firstName); // "Steve"
console.log(thirdName); // "Sam"
const scores = [87, 43, 88, 99];
console.log(scores[0]); // 87
console.log(scores[1]); // 43
console.log(scores[2]); // 88
console.log(scores.at(-1)); // 99
console.log(scores.at(99)); // undefinedUse at(-1) to read the last element without calculating length - 1. Negative indexes count backward with at(); bracket notation such as scores[-1] doesn't mean “last element.” An out-of-range at() call returns undefined.
Looping Through Array Elements
forEach(): Perform an Action for Each Element
A callback is a function passed to another operation. forEach() calls yours for each populated element, discards its return value, and returns undefined. Use it for side effects such as logging, not to build a transformed array.
const scores = [87, 43, 88, 99];
const result = scores.forEach((score) => {
console.log(score);
});
// Logs 87, 43, 88, 99 on separate lines.
console.log(result); // undefinedCommon callbacks, including those for forEach(), map(), and filter(), receive (value, index, array). You can omit arguments you don't need. The array argument is the receiver—the array the method was called on—not the result being constructed. See JavaScript functions for more on callback syntax.
for...of and Indexed Loops: Control Iteration
Prefer for...of when you want values and ordinary loop control such as break. Use an indexed for loop when you need explicit control over the position or step size. forEach() doesn't support breaking out with break.
const scores = [87, 43, 88, 99];
for (const score of scores) {
if (score < 50) break;
console.log(score); // Logs only 87 before the loop stops.
}
for (let index = 0; index < scores.length; index += 2) {
console.log(scores[index]); // Logs 87, then 88.
}Our JavaScript loops tutorial explains these loop forms in more detail.
entries() and keys(): Consume Array Iterators
An iterator produces successive results when you call next(). entries() yields [index, value] pairs, while keys() yields indexes. Each next() result has value and done properties; done becomes true when an array iterator is exhausted.
const scores = [87, 43, 95, 88, 99];
for (const [index, score] of scores.entries()) {
console.log(index, score);
}
// Logs 0 87, 1 43, 2 95, 3 88, 4 99 on separate lines.
const entries = scores.entries();
console.log(entries.next()); // { value: [0, 87], done: false }
console.log(entries.next()); // { value: [1, 43], done: false }
const keys = scores.keys();
console.log(keys.next()); // { value: 0, done: false }
const emptyIterator = [].entries();
console.log(emptyIterator.next()); // { value: undefined, done: true }JavaScript Array Methods: Mutation at a Glance
Mutation means changing an existing array rather than returning an independent array container. The table describes the methods themselves; a callback you write can still modify the input or objects inside it.
| Purpose | Methods | Result | Changes the original? |
|---|---|---|---|
| Transform or select | map(), filter() | New array | No |
| Combine, copy, or flatten | concat(), slice(), flat(), flatMap() | New array | No |
| Search | includes(), indexOf(), find(), findIndex(), findLast(), findLastIndex() | Boolean, value, or index | No |
| Test or accumulate | every(), some(), reduce() | Boolean or accumulated result | No, unless your callback mutates it |
| Perform side effects | forEach() | undefined | No, unless your callback mutates it |
| Reorder | sort(), reverse() | Same array | Yes |
| Insert, remove, or replace | splice() | Removed elements as an array | Yes |
| Edit by copying | toSorted(), toReversed(), toSpliced(), with() | New array | No |
Transforming, Filtering, and Combining Arrays
map() and filter(): Return New Arrays
map() builds its result from your callback's return values. filter() keeps the original values whose callback result is truthy—a value treated as true in a condition. Neither method itself changes the original array.
const scores = [87, 43, 88, 99];
const incrementedScores = scores.map((score) => score + 10);
const filteredScores = scores.filter((score) => score > 80);
const labels = scores
.filter((score) => score > 80)
.map((score) => `Score: ${score}`);
console.log(incrementedScores); // [97, 53, 98, 109]
console.log(filteredScores); // [87, 88, 99]
console.log(labels); // ["Score: 87", "Score: 88", "Score: 99"]
console.log(scores); // [87, 43, 88, 99]The chain reads as “select scores above 80, then format them.” Each stage creates a new array. Start with the readable chain; if intermediate allocations matter in a measured hot path, consider a single loop.
concat() and slice(): Combine or Extract
concat() combines arrays without changing its inputs. slice(start, end) returns a section with the end index excluded; don't confuse it with the mutating splice() method.
const scores = [87, 43, 88, 99];
const moreScores = [100, 33, 78, 44];
const allScores = scores.concat(moreScores);
console.log(allScores); // [87, 43, 88, 99, 100, 33, 78, 44]
console.log(scores.slice(1, 3)); // [43, 88]flat() and flatMap(): Handle Nested Results
flat() flattens nested arrays by one level by default; pass a depth to flatten further. flatMap() maps values and flattens the mapped result by exactly one level. Both return new arrays.
console.log([[87, 43], [88, 99]].flat());
// [87, 43, 88, 99]
console.log([1, [2, [3]]].flat(2));
// [1, 2, 3]
console.log([1, 2].flatMap((value) => [value, value * 10]));
// [1, 10, 2, 20]Searching, Testing, and Reducing Arrays
includes() and indexOf(): Search for a Value
Use includes() when you only need a membership check. Use indexOf() when you need the first matching index; it returns -1 when no match exists.
const scores = [87, 43, 88, 99];
console.log(scores.includes(88)); // true
console.log(scores.indexOf(88)); // 2
console.log(scores.indexOf(103)); // -1find(), findIndex(), findLast(), and findLastIndex()
Use a predicate—a callback that tests a condition—when matching isn't simply about an exact value. find() returns the first matching value, while findIndex() returns its index. Their findLast() counterparts search backward, starting at the highest index.
const scores = [87, 43, 95, 88, 99];
console.log(scores.find((score) => score > 90)); // 95
console.log(scores.findIndex((score) => score > 90)); // 2
console.log(scores.findLast((score) => score > 90)); // 99
console.log(scores.findLastIndex((score) => score > 90)); // 4
console.log(scores.find((score) => score > 100)); // undefined
console.log(scores.findIndex((score) => score > 100)); // -1The value-returning methods return undefined when nothing matches; the index-returning methods return -1. Choose filter() instead when you need every matching value in an array.
every() and some(): Test Conditions
every() checks whether all populated elements pass a test; some() checks whether at least one passes. They stop once the answer is known. On an empty array, every() returns true and some() returns false.
const scores = [87, 43, 88, 99];
console.log(scores.every((score) => score > 10)); // true
console.log(scores.some((score) => score < 50)); // truereduce(): Accumulate a Result
reduce() passes an accumulator—the result so far—to your callback alongside the current value. Return the next accumulator on each call. Provide an initial value so the operation also handles empty input; reducing an empty array without one throws a TypeError.
const count = [1, 2, 3, 4];
const sum = count.reduce((total, value) => total + value, 0);
console.log(sum); // 10
console.log([].reduce((total, value) => total + value, 0)); // 0Sorting and Editing Arrays
sort(): Use a Comparator for Numeric Order
Without a comparator, sort() converts non-undefined values to strings and compares their UTF-16 code units. That's why 100 can appear before 20. For ordinary numbers, use (a, b) => a - b for ascending order or (a, b) => b - a for descending order.
const scores = [87, 43, 88, 99, 100, 20];
console.log([...scores].sort());
// [100, 20, 43, 87, 88, 99]: string-based ordering
console.log([...scores].sort((a, b) => a - b));
// [20, 43, 87, 88, 99, 100]: numeric ascending
console.log(scores.toSorted((a, b) => b - a));
// [100, 99, 88, 87, 43, 20]: numeric descending
console.log(scores);
// [87, 43, 88, 99, 100, 20]: unchangedsort() mutates the array it receives and returns that same array. The spread syntax above creates a copy before sorting. toSorted() expresses the copying operation directly, but it still needs a comparator for numeric ordering.
splice(): Inspect the Return Value and the Original
splice(start, deleteCount, ...items) edits an array in place and returns an array of removed elements. With a delete count of zero, it inserts without removing anything.
const scores = [87, 43, 88, 99];
const removed = scores.splice(2, 0, 95);
console.log(removed); // []
console.log(scores); // [87, 43, 95, 88, 99]
const replaced = scores.splice(1, 2, 100);
console.log(replaced); // [43, 95]
console.log(scores); // [87, 100, 88, 99]Modern Copying Methods: toReversed(), toSpliced(), and with()
Use toReversed() to reverse a copy, toSpliced() to edit a copy, and with(index, value) to replace an element in a copy. Unlike splice(), toSpliced() returns the edited array, not the removed elements.
const scores = [87, 43, 88, 99];
console.log(scores.toReversed()); // [99, 88, 43, 87]
console.log(scores.toSpliced(2, 0, 95)); // [87, 43, 95, 88, 99]
console.log(scores.with(1, 100)); // [87, 100, 88, 99]
console.log(scores); // [87, 43, 88, 99]with() accepts negative indexes, but an index outside the array's bounds throws a RangeError. It replaces an existing position; it isn't an append operation.
Common Mistakes
- Assuming const prevents mutation. It prevents reassignment of the variable, not changes to the array's contents.
- Forgetting a map() return value. A block-bodied callback needs an explicit return to supply each mapped result.
- Using indexOf() as a Boolean. Compare its result with -1; a match at index 0 is still a match.
- Expecting forEach() to wait. It doesn't await promises returned by async callbacks. Choose an iteration strategy that explicitly coordinates asynchronous work.
- Overlooking other mutators. push(), pop(), shift(), unshift(), fill(), and copyWithin() also modify the original array.
FAQ
How do you sort numbers numerically in JavaScript?
Use sort((a, b) => a - b) for ascending numeric order, or reverse the subtraction for descending order. sort() changes the original array; toSorted() accepts the same comparator and returns a new array.
Which JavaScript array methods change the original array?
Common mutating methods include sort(), reverse(), splice(), push(), pop(), shift(), unshift(), fill(), and copyWithin(). Copying alternatives include toSorted(), toReversed(), toSpliced(), and with(); map(), filter(), and concat() also return new arrays.
What is the difference between map() and forEach()?
map() returns a new array built from the callback's return values, so use it for transformation. forEach() runs the callback for side effects, discards callback return values, and returns undefined.
What is the difference between new Array(4) and [4]?
new Array(4) creates an array with length 4 and four empty slots. [4] creates an array with length 1 whose only element is the number 4. Prefer literals for ordinary array creation.
When should you use find() instead of filter()?
Use find() when you want the first matching element; it stops at that match and returns undefined if none exists. Use filter() when you want an array of all matches, which is empty when nothing matches.
Further Reading
- MDN Array reference — the method catalog, sparse arrays, and mutation behavior.
- MDN Array constructor — constructor overloads and empty-slot examples.
- MDN map() — callback arguments, return values, and transformation examples.
- MDN forEach() — side effects, early-exit limitations, and asynchronous pitfalls.
- MDN sort() — default ordering and comparator requirements.
- MDN splice() — insertion, removal, replacement, and return values.
- MDN toSpliced() — editing by copying and differences from splice().
- ECMAScript 2023 indexed collections specification — precise array-method algorithms.
