053: Array Methods I: Mutation, Copying, and Search
Outcomes
By the end of this lesson, you can:
- add/remove end elements with
push()andpop(); - add/remove beginning elements with
unshift()andshift(); - copy a range with non-mutating
slice(); - insert, replace, or remove a range with mutating
splice(); - test membership with
includes()and locate values withindexOf(); - state each method's return value; and
- choose intentionally between mutation and a new array.
Prerequisites and Retrieval
Before working through these methods, retrieve the basics: array literals, indexes, length, mutation, and references.
- What is the first array index?
- Does
const items = []preventitemsfrom being mutated? - Why is
items[items.length]not the final current element?
For today's examples, use arrays of primitive values. Object identity and shallow copies matter more once we reach the later data lessons.
Terms
- method: Function stored as an object/array property invoked via dot access. — Source: MDN: Method
- mutating method: Changes the receiver array (push/pop/splice/sort/reverse). — Source: MDN: Array — mutating methods
- copying/non-mutating method: Returns a new array leaving the original intact (slice, concat, toSorted). — Source: MDN: Array
- start index: First position included by slice(begin). — Source: MDN: Array.prototype.slice()
- end index: Exclusive boundary where slice stops extracting. — Source: MDN: Array.prototype.slice()
- delete count: Second argument to splice counting removals. — Source: MDN: Array.prototype.splice()
- membership: Testing presence via includes()/indexOf(). — Source: MDN: Array.prototype.includes()
- sentinel: Special return value meaning absence, e.g., indexOf() → -1. — Source: MDN: Array.prototype.indexOf()
- shallow copy: New outer array/object sharing nested references (slice, [...arr]). — Source: MDN: Spread syntax
- String (official): "A string is a sequence of characters used to represent text." — Source: MDN: String
- Template literal (official): "Template literals are string literals allowing embedded expressions using backticks and ${}." — Source: MDN: Template literals
Beginner Explanation and Mental Model
Array methods are functions attached to an array. When you reach for one, do not stop at remembering its name. Ask three practical questions:
- Does it mutate the original array?
- What arguments does it expect?
- What does it return?
The return value is a frequent source of bugs. Similar-looking methods can return a new length, a removed element, a removed-elements array, or a search result.
| Method | Main action | Mutates? | Return value |
|---|---|---|---|
push(a) | add to end | yes | new length |
pop() | remove from end | yes | removed element or undefined |
unshift(a) | add to beginning | yes | new length |
shift() | remove from beginning | yes | removed element or undefined |
slice(start, end) | copy included range | no | new array |
splice(start, count, ...items) | remove/insert in place | yes | array of removed elements |
includes(value) | ask whether value exists | no | Boolean |
indexOf(value) | find first matching index | no | index or -1 |
End and beginning operations
const queue = ["A", "B"];
const lengthAfterPush = queue.push("C"); // queue is A,B,C; returns 3
const last = queue.pop(); // queue is A,B; returns C
const lengthAfterUnshift = queue.unshift("Start"); // returns 3
const first = queue.shift(); // returns Start
All four methods mutate queue. Adding or removing at the beginning also requires existing indexes to move. That makes end operations a simpler fit when both choices express the same data model. The decision should still come from the meaning of the data, not from micro-optimization alone.
slice: end excluded
const letters = ["a", "b", "c", "d"];
const middle = letters.slice(1, 3); // ["b", "c"]
Index 1 is included, while index 3 is the stopping boundary and is excluded. letters is unchanged. If end is omitted, slice() copies through the end; with no arguments, it makes a shallow outer copy. Negative indexes count backward from the end.
splice: edit in place
const letters = ["a", "b", "c", "d"];
const removed = letters.splice(1, 2, "x", "y");
// letters: ["a", "x", "y", "d"]
// removed: ["b", "c"]
Read this call as: start at index 1, remove 2 elements, and insert the remaining arguments in their place. splice(index, 1) removes one known index. splice(index, 0, value) inserts a value without removing anything. The second argument is not an end index; it is a count of elements to remove.
Search
Use includes() when the question is simply whether a value is present. Use indexOf() when you need the first matching position. If there is no match, indexOf() returns the sentinel value -1:
const roles = ["viewer", "editor"];
console.log(roles.includes("editor")); // true
console.log(roles.indexOf("editor")); // 1
console.log(roles.indexOf("admin")); // -1
For ordinary primitive values, both methods use essentially strict value matching. One notable exception is NaN: includes(NaN) can find it, whereas indexOf(NaN) returns -1. For beginner lists, keep searches focused on stable primitive values.
Worked Example: Task Queue
const tasks = ["Write code", "Run tests"];
const newLength = tasks.push("Review output");
console.log("After push:", tasks);
console.log("New length:", newLength);
tasks.unshift("Read requirements");
console.log("After unshift:", tasks);
const currentTask = tasks.shift();
console.log("Started:", currentTask);
const completedTask = tasks.pop();
console.log("Removed from end:", completedTask);
console.log("Remaining:", tasks);
console.log("Contains tests:", tasks.includes("Run tests"));
console.log("Tests index:", tasks.indexOf("Run tests"));
Expected output:
After push: ["Write code", "Run tests", "Review output"]
New length: 3
After unshift: ["Read requirements", "Write code", "Run tests", "Review output"]
Started: Read requirements
Removed from end: Review output
Remaining: ["Write code", "Run tests"]
Contains tests: true
Tests index: 1
The exact way a browser formats an array in the console can vary. The behavior does not: push() returns the new length, not the item that was added; shift() and pop() return the individual values they remove. The search calls do not alter the remaining array.
Intermediate Example: Copy and Edit Product Names
const products = ["Mouse", "Keyboard", "Monitor", "Webcam", "Headset"];
const featured = products.slice(1, 4);
console.log("Featured:", featured);
console.log("Original after slice:", products);
const removedProducts = products.splice(2, 2, "Laptop stand", "USB hub");
console.log("Removed:", removedProducts);
console.log("Original after splice:", products);
const searchName = "USB hub";
const searchIndex = products.indexOf(searchName);
if (searchIndex !== -1) {
console.log(`${searchName} found at index ${searchIndex}.`);
} else {
console.log(`${searchName} not found.`);
}
Expected output:
Featured: ["Keyboard", "Monitor", "Webcam"]
Original after slice: ["Mouse", "Keyboard", "Monitor", "Webcam", "Headset"]
Removed: ["Monitor", "Webcam"]
Original after splice: ["Mouse", "Keyboard", "Laptop stand", "USB hub", "Headset"]
USB hub found at index 3.
The guard around splice() is deliberate. Never write if (products.indexOf(searchName)): index 0 is falsy even though it means “found,” while -1 is truthy even though it means “absent.” Compare explicitly with !== -1, or use includes() if you only need the membership answer.
Optional Advanced Extension: Non-Mutating Splice Alternative
Modern stable JavaScript also provides toSpliced(). It accepts splice-like arguments but returns a new array instead of editing the original:
const original = ["a", "b", "c"];
const updated = original.toSpliced(1, 1, "x");
console.log(original); // ["a", "b", "c"]
console.log(updated); // ["a", "x", "c"]
This is useful when callers expect immutable-style updates. The required method for this lesson is still splice(), so first become comfortable with its mutation and return value. Also, do not claim that slice() can directly insert replacements: it only copies ranges.
Mistakes and Debugging
- Assigning
pushresult as the array:items = items.push(value)makesitemsa number. Callitems.push(value)and keep the original binding. - Expecting
pop/shiftto return an array: each returns one removed element. - Confusing
sliceandsplice:slicecopies;spliceedits. - Treating
sliceend as included: it is excluded. - Treating
splicedelete count as end index: it is how many to remove. - Checking
indexOfby truthiness: compare with-1. - Splicing at
-1after a failed search: this edits from the end. Verify the index first. - Expecting copying methods to deep-clone objects: standard array copy operations are shallow.
- Ignoring mutation in a function: document mutation or return a new array so callers know what happens.
When a method behaves unexpectedly, log two things before and after the call: the array itself and the return value captured from the method. Separating those observations usually makes it clear whether the misunderstanding is about mutation, the selected range, or the return type.
Best Practices
- Choose a method by required semantics, including mutation and return value.
- Use
includes()for yes/no membership andindexOf()when the position is needed. - Check
indexOf()against-1before callingsplice(). - Store removed values when they matter.
- Use
slice()for ranges and shallow outer copies. - Keep
splice()arguments in named variables for nontrivial edits. - Prefer non-mutating operations when shared references or application state make mutation surprising.
- Use
constfor the binding even when deliberately mutating the same array.
Tiered Exercises
Core
Start with ["CSS", "JavaScript"]. Add "HTML" to the beginning, add "Git" to the end, remove both ends while storing returned values, and log every intermediate array and return value.
Practice
Given const cities = ["Chennai", "Madurai", "Salem", "Trichy", "Coimbatore"], copy indexes 1-3 with slice, replace "Salem" and "Trichy" with "Erode" using splice, then search for "Erode" and "Salem".
Professional Extension
Write removeItem(items, item) that mutates the supplied array only if item exists. It should return the removed value, or null when absent. Test an item at index 0, a middle item, and an absent item.
Complete Solutions
const topics = ["CSS", "JavaScript"];
const frontLength = topics.unshift("HTML");
console.log(topics, frontLength);
const endLength = topics.push("Git");
console.log(topics, endLength);
const first = topics.shift();
console.log(topics, first);
const last = topics.pop();
console.log(topics, last);
Final array: ["CSS", "JavaScript"]; removed values: "HTML" and "Git".
const cities = ["Chennai", "Madurai", "Salem", "Trichy", "Coimbatore"];
const middleCities = cities.slice(1, 4);
const removedCities = cities.splice(2, 2, "Erode");
console.log(middleCities); // Madurai, Salem, Trichy
console.log(removedCities); // Salem, Trichy
console.log(cities); // Chennai, Madurai, Erode, Coimbatore
console.log(cities.includes("Erode")); // true
console.log(cities.indexOf("Erode")); // 2
console.log(cities.includes("Salem")); // false
console.log(cities.indexOf("Salem")); // -1
function removeItem(items, item) {
const index = items.indexOf(item);
if (index === -1) {
return null;
}
const removedItems = items.splice(index, 1);
return removedItems[0];
}
const values = ["a", "b", "c", "d"];
console.log(removeItem(values, "a")); // a
console.log(removeItem(values, "c")); // c
console.log(removeItem(values, "z")); // null
console.log(values); // ["b", "d"]
Checking -1 prevents an absent item from accidentally removing the last element.
Recap and Exit Questions
Array methods differ in three ways you should keep separate: what they do, whether they mutate, and what they return. The end and beginning methods mutate. slice() copies an end-exclusive range. splice() edits in place and returns removed elements. includes() answers a membership question, while indexOf() returns a position or -1.
- Which required methods mutate the original array?
- What do
push()andpop()return? - Why does
slice(1, 3)omit index 3? - What does the second
spliceargument mean? - Why is
if (items.indexOf(value))incorrect?
Official References
- MDN:
Array - MDN: Indexed collections, array methods
- MDN:
slice() - MDN:
splice() - MDN:
includes() - MDN:
indexOf() - ECMA-262: properties of the Array prototype object
References checked 2026-08-24.
