What you'll be able to do

By the end of this page you should be able to copy a portion of a list with slice, join lists with concat (or spread), and use splice only when you mean to mutate — plus tell slice and splice apart by name and behavior.

Previous post: reduce from zero.

Who this is for

  • People who mix up slice and splice because the names look alike
  • Beginners who call splice expecting a harmless copy
  • Anyone building “page 2” of a list and needing a window of items

You can skip this if you already reach for slice/concat for copies and splice only for intentional edits. Come back when a shared array loses items after a “copy.”

slice: copy a window (does not mutate)

slice(start, end) returns a new array from start up to (not including) end. Omit end to go to the end. Negative indexes count from the end.

const tags = ["a", "b", "c", "d"];
console.log(tags.slice(1, 3)); // ["b", "c"]
console.log(tags.slice()); // full shallow copy
console.log(tags); // unchanged

Scenario A — first three search results. results.slice(0, 3).

Scenario B — real copy before experimenting. const copy = list.slice() — same idea as earlier mutation lessons.

concat: join into a new array

const a = [1, 2];
const b = [3, 4];
const all = a.concat(b);
console.log(all); // [1, 2, 3, 4]
console.log(a); // [1, 2]

Scenario A — merge default tags with user tags. New list; originals stay.

Scenario B — modern style. [...a, ...b] does the same for most beginner cases.

concat does not mutate a or b.

splice: change the array in place

splice(start, deleteCount, ...itemsToInsert) mutates the original array. It also returns an array of the removed items.

const list = ["a", "b", "c", "d"];
const removed = list.splice(1, 2, "X");
console.log(removed); // ["b", "c"]
console.log(list); // ["a", "X", "d"]

Scenario A — delete one todo by index. todos.splice(i, 1) after findIndex.

Scenario B — insert without deleting. list.splice(2, 0, "new") — deleteCount 0.

If you only wanted a copy of a range, use slice. splice will shrink or rewrite the shared list.

Name trap: slice vs splice

MethodMutates?Typical job
sliceNoCopy / window
spliceYesDelete / insert in place
concatNoJoin lists into a new one

Scenario A — typo in a PR. splice(0) with no deleteCount can remove everything from the start — painful on a shared cart.

Scenario B — safe habit. Prefer slice + concat/spread when building new arrays for UI state.

A tiny practice file

Save as slice-splice.html.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>slice splice concat</title>
  </head>
  <body>
    <button id="slice">slice copy</button>
    <button id="splice">splice mutate</button>
    <button id="concat">concat join</button>
    <pre id="out"></pre>
    <script>
      let base = ["a", "b", "c", "d"];
      const out = document.getElementById("out");
      function show(label, extra) {
        out.textContent = label + "\nbase: " + JSON.stringify(base) + (extra ? "\n" + extra : "");
      }
      document.getElementById("slice").addEventListener("click", function () {
        base = ["a", "b", "c", "d"];
        const part = base.slice(1, 3);
        show("slice(1,3)", "part: " + JSON.stringify(part));
      });
      document.getElementById("splice").addEventListener("click", function () {
        base = ["a", "b", "c", "d"];
        const removed = base.splice(1, 2, "X");
        show("splice(1,2,'X')", "removed: " + JSON.stringify(removed));
      });
      document.getElementById("concat").addEventListener("click", function () {
        base = ["a", "b"];
        const all = base.concat(["c"]);
        show("concat", "all: " + JSON.stringify(all));
      });
    </script>
  </body>
</html>

Watch base stay put after slice/concat, and change after splice.

Mistakes I see a lot

1. Using splice when you meant slice. Shared data disappears.

2. Ignoring the return value of splice. Removals come back as an array; the mutated list is the original variable.

3. Forgetting slice’s end is exclusive. slice(0, 1) is one item.

4. Mutating with splice inside a map. Hard to reason about; prefer building a new array.

5. Assuming concat flattens deeply. It does not deep-flatten nested arrays.

6. list2 = list1.slice() then mutating nested objects. Shallow copy — same caveat as before.

What to try before the next post

  1. slice the middle of a four-item list.
  2. concat two arrays; confirm originals.
  3. splice delete one index; log both return value and array.
  4. Build the tiny HTML file.

Next in this series: sort and common mistakes — why numbers sort as strings until you compare them yourself.

Try this next outside the series

Arrays get easier when you see them render UI lists and move through real transforms.