Module: JavaScript
JavaScript·053·6 MIN READ

053: Array Methods I: Mutation, Copying, and Search

TOPICS COVERED: Array Methods I: Mutation, Copying, and Search

Outcomes

By the end of this lesson, you can:

  • add/remove end elements with push() and pop();
  • add/remove beginning elements with unshift() and shift();
  • copy a range with non-mutating slice();
  • insert, replace, or remove a range with mutating splice();
  • test membership with includes() and locate values with indexOf();
  • state each method's return value; and
  • choose intentionally between mutation and a new array.

Prerequisites and Retrieval

Retrieve array literals, indexes, length, mutation, and references.

  1. What is the first array index?
  2. Does const items = [] prevent items from being mutated?
  3. Why is items[items.length] not the final current element?

Use arrays of primitive values today. Object identity and shallow copies become more important in later data lessons.

Terms

Beginner Explanation and Mental Model

Array methods are tools attached to arrays. For each tool ask three questions:

  1. Does it mutate the original array?
  2. What arguments does it expect?
  3. What does it return?

Memorizing only a method name causes bugs because return values differ.

MethodMain actionMutates?Return value
push(a)add to endyesnew length
pop()remove from endyesremoved element or undefined
unshift(a)add to beginningyesnew length
shift()remove from beginningyesremoved element or undefined
slice(start, end)copy included rangenonew array
splice(start, count, ...items)remove/insert in placeyesarray of removed elements
includes(value)ask whether value existsnoBoolean
indexOf(value)find first matching indexnoindex or -1

End and beginning operations

js
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 mutate. Adding/removing at the beginning requires existing indexes to move, so end operations are often a simpler fit when either model is acceptable. Choose based on data semantics, not micro-optimization alone.

slice: end excluded

js
const letters = ["a", "b", "c", "d"];
const middle = letters.slice(1, 3); // ["b", "c"]

Index 1 is included; index 3 is excluded. The original does not change. Omitting end copies to the end; no arguments makes a shallow outer copy. Negative indexes count from the end.

splice: edit in place

js
const letters = ["a", "b", "c", "d"];
const removed = letters.splice(1, 2, "x", "y");
// letters: ["a", "x", "y", "d"]
// removed: ["b", "c"]

Read it as: at index 1, remove 2 elements, then insert the remaining arguments. Use splice(index, 1) to remove one known index and splice(index, 0, value) to insert without removal. Do not confuse its second argument with an end index; it is a count.

includes() answers a yes/no question. indexOf() answers where the first match occurs and returns -1 if absent:

js
const roles = ["viewer", "editor"];
console.log(roles.includes("editor")); // true
console.log(roles.indexOf("editor"));  // 1
console.log(roles.indexOf("admin"));  // -1

Both compare essentially by strict value matching for normal primitives, with one notable difference: includes(NaN) can find NaN, while indexOf(NaN) returns -1. For beginner lists, search stable primitive values.

Worked Example: Task Queue

js
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:

text
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

Browser array formatting varies. Notice that push did not return the added item; it returned length. shift and pop returned removed values. Search methods left the remaining array untouched.

Intermediate Example: Copy and Edit Product Names

js
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:

text
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.

Never write if (products.indexOf(searchName)). Index 0 is falsy even though it means found, and -1 is truthy even though it means absent. Compare explicitly with !== -1, or use includes() when only membership matters.

Optional Advanced Extension: Non-Mutating Splice Alternative

Modern stable JavaScript includes toSpliced(), which uses splice-like arguments but returns a new array:

js
const original = ["a", "b", "c"];
const updated = original.toSpliced(1, 1, "x");

console.log(original); // ["a", "b", "c"]
console.log(updated);  // ["a", "x", "c"]

It is useful when callers expect immutable-style updates. Today's required method is splice(), so first master its mutation and return value. Do not claim slice() can directly insert replacements; it only copies ranges.

Mistakes and Debugging

  • Assigning push result as the array: items = items.push(value) makes items a number. Call items.push(value) and keep the original binding.
  • Expecting pop/shift to return an array: each returns one removed element.
  • Confusing slice and splice: slice copies; splice edits.
  • Treating slice end as included: it is excluded.
  • Treating splice delete count as end index: it is how many to remove.
  • Checking indexOf by truthiness: compare with -1.
  • Splicing at -1 after 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.

Before and after a method call, log the array and captured return value separately. This reveals most method misunderstandings immediately.

Best Practices

  • Choose a method by required semantics, including mutation and return value.
  • Use includes() for yes/no membership and indexOf() when the position is needed.
  • Check indexOf() against -1 before calling splice().
  • 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 const for 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

js
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".

js
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
js
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

Methods differ in action, mutation, and return value. End/front methods mutate, slice copies an end-exclusive range, splice edits in place and returns removed elements, includes answers membership, and indexOf returns a position or -1.

  1. Which required methods mutate the original array?
  2. What do push() and pop() return?
  3. Why does slice(1, 3) omit index 3?
  4. What does the second splice argument mean?
  5. Why is if (items.indexOf(value)) incorrect?

Official References

References checked 2026-08-24.