Why Array.prototype.sort mutates in place and returns the same reference — the silent aliasing bug that corrupts your "original" array after sorting in production JavaScript.
This post explains a quiz originally shared as a LinkedIn poll.
🔹 The Question
const scores = [85, 92, 78, 95, 88];
const ranked = scores.sort((a, b) => b - a);
scores.push(70);
console.log(ranked.length);
console.log(ranked === scores);
console.log(ranked[0]);
Hint: sort() is one of the few array methods that does not return a new array. Think carefully about what ranked actually is before the push happens.
Follow me for JavaScript puzzles and weekly curations of developer talks & insights at Talk::Overflow: https://talkoverflow.substack.com/
🔹 Solution
Correct answer: A) 6, true, 95
The output is:
6
true
95
🧠 How this works
This quiz exposes one of the most common and silent bugs in JavaScript array handling: Array.prototype.sort sorts the array in place and returns a reference to the same array — not a new one. This behavior is fundamentally different from the functional array methods developers use daily (map, filter, slice, concat, flat), all of which return new arrays and leave the original untouched.
When scores.sort(...) is called, two things happen simultaneously:
- The original
scoresarray is mutated — its elements are reordered in descending order. - The return value is the exact same array object that was sorted — not a copy.
Assigning the return value to ranked does not produce a second array. ranked is just another name for scores. They are the same object in memory. Any mutation applied to one is immediately visible through the other — including the scores.push(70) that comes next.
This is why ranked.length is 6 instead of 5, ranked === scores is true (strict equality checks object identity), and ranked[0] is 95 (the highest score after the descending sort).
🔍 Line-by-line explanation
const scores = [85, 92, 78, 95, 88]— Creates an array of five numbers. Let's call this Object A.const ranked = scores.sort((a, b) => b - a)— Sorts Object A in place in descending order:[95, 92, 88, 85, 78].sort()returns a reference to Object A.rankednow points to the same Object A. There is still only one array in memory. Bothscoresandrankedare aliases for it.scores.push(70)— Appends70to Object A. Sincerankedpoints to the same Object A,rankedis now[95, 92, 88, 85, 78, 70]and itslengthis6.console.log(ranked.length)—rankedis Object A, which now has 6 elements. Prints6.console.log(ranked === scores)—===on objects checks reference identity.rankedandscorespoint to the same object. Printstrue.console.log(ranked[0])— The first element of the descending-sorted array is95. Prints95.
The non-obvious part: The developer's mental model is reasonable — in most modern JavaScript, array transformation methods return new arrays. map, filter, reduce, slice, concat, flat, flatMap — all of these give you a new array and preserve the original. sort (and reverse and splice) are the legacy exceptions that mutate in place. The return value of sort() looks like a "sorted copy" in assignment syntax (const ranked = scores.sort(...)), but it's actually the sorted original handed back.
Since ES2023, Array.prototype.toSorted() provides a non-mutating alternative that genuinely returns a new sorted array. The asymmetry between sort and toSorted exists precisely because the original sort behavior is a well-known footgun.
🔹 Real-World Impact
Where this appears:
Table sorting in dashboards: A developer fetches a list of records, stores it as
originalData, and sorts a "view" copy for display:const sorted = originalData.sort(compareFn). When the user resets filters,originalDatais already mutated and sorted — the "original" order is gone. The reset button does nothing meaningful.Leaderboard / ranking features: A game or analytics app maintains a live scores array. A function computes
const topFive = scores.sort((a, b) => b - a).slice(0, 5). The.sort()mutates the livescoresarray, causing all downstream consumers ofscoresto see it in sorted order rather than insertion order (e.g., recent-activity feeds, historical charts).Pagination logic: An app applies
.sort()to paginate data on the frontend. TheallItemsarray used by the page-count calculation is now in sorted order, which may not matter for counts — but if another component iteratesallItemsexpecting insertion order (e.g., to highlight "newly added" items), it silently gets the wrong order.Test isolation failures: A test creates a shared fixture array, sorts it in one test, then expects it to be in original order in a later test. Because
sortmutated in place, subsequent tests fail inconsistently depending on execution order — a classic shared-mutable-state test pollution scenario.
🔹 Key Takeaways
Array.prototype.sortmutates the original array and returns the same reference. Unlikemap,filter,slice, andconcat,sortdoes not create a new array. Assigning the return value to a new variable creates an alias, not a copy.Always copy before sorting if you need the original order preserved. Use
[...arr].sort(compareFn),arr.slice().sort(compareFn), orArray.from(arr).sort(compareFn)to sort a copy. This is an extremely common defensive pattern in production code.Array.prototype.toSorted()(ES2023) is the non-mutating alternative.arr.toSorted(compareFn)returns a new sorted array and leavesarruntouched. Prefer it in new code when targeting modern environments. Similarly,toReversed()andtoSpliced()are the non-mutating versions ofreverseandsplice.Array.prototype.reversehas the same mutation behavior. It reorders elements in place and returns the same array.const reversed = arr.reverse()is an alias, not a copy.arr.toReversed()is the safe alternative.Strict equality (
===) on objects checks identity, not structure.ranked === scoresbeingtrueis the diagnostic tell. When two variables point to the same object, mutations through either variable affect both. If you suspect aliasing,===confirms it; use it in debugging before reaching for deep-equality utilities.
Top comments (0)