Before any individual method, there's a property every array method has that matters more than what it's called: does it change the array you already have, or does it hand you back a different one and leave yours alone?
Watch what happens when the dashboard tries to build a "Top Scorers" leaderboard from candidates:
const leaderboard = candidates.sort((a, b) => b.score - a.score);
console.log(leaderboard === candidates); // true — not a copy, the SAME array
console.log(candidates.map(c => c.name));
// ["Amara Chen", "Priya Shah", "Sam Okafor", "Diego Ramos", "Lena Fischer"]
That looks fine in isolation — the leaderboard is sorted correctly. The problem shows up somewhere else on the same page: a "Recently Applied" panel that also reads from candidates, expecting it in the order people actually applied. It's now silently showing the leaderboard order instead, because .sort() didn't build a new array for the leaderboard — it rearranged the one and only candidates array in place and handed back a reference to that same array. Two features that had no business knowing about each other are now coupled through a side effect neither of them wrote.
It's also the exact bug that shows up in React and other frameworks built around this same idea: a component decides whether it needs to re-render by checking if a prop or piece of state now points at a different object than it did last time — it never actually walks the contents looking for changes. Mutate candidates in place and you've kept the same reference the whole time, so as far as the framework can tell, nothing happened — the screen just doesn't update, even though the underlying data plainly did.
The fix here is to make a copy before reordering it:
const leaderboard = candidates.slice().sort((a, b) => b.score - a.score);
// or, in a runtime that has it:
const leaderboard = candidates.toSorted((a, b) => b.score - a.score);
console.log(leaderboard === candidates); // false — a real, separate array
Knowing which category a method falls into is worth memorizing outright, because the method names give you no hint:
| Mutates the array you called it on | Leaves your array alone, gives you a new one |
|---|---|
.push(), .pop(), .shift(), .unshift() | .map(), .filter(), .reduce() |
.splice(), .sort(), .reverse() | .slice(), .concat(), .flat(), .flatMap() |
.fill(), .copyWithin() | spread ([...arr]), .toSorted(), .toReversed(), .toSpliced(), .with() |
That bottom-right group — .toSorted(), .toReversed(), .toSpliced(), .with() — is newer: non-mutating versions of the classic mutating methods, added specifically so you don't have to remember to .slice() first. They need a reasonably current JavaScript engine, so check what you're targeting before you commit to them in anything that still has to run on older browsers.