sort()
ES3+Sorts the elements of an array in place and returns the sorted array.
Syntax
array.sort(compareFunction)Parameters
compareFunction Function optionalFunction that defines the sort order
Return Value
The sorted array
Examples
const numbers = [3, 1, 4, 1, 5, 9];
numbers.sort((a, b) => a - b);
console.log(numbers); 📌 When to Use
Use sort() whenever elements must be arranged by some ordering rule: product lists by price, tables by whichever column the user clicked, events by date, search results by relevance score. The critical mental model is that sort() without arguments does NOT sort "naturally" - it converts every element to a string and compares UTF-16 code units, which alphabetizes plain lowercase words correctly but mangles numbers ([10, 9, 2] becomes [10, 2, 9]), puts all uppercase letters before all lowercase ones, and misorders accented characters. So the practical rule is: always pass a comparator except for the narrow case of simple ASCII strings. Numbers take (a, b) => a - b; strings that face real users take localeCompare() or a reusable Intl.Collator; dates compare via their numeric timestamps. The second decision is mutation. sort() rearranges the array in place and returns the same reference, which is fine for arrays you just built, but for React/Vue/Svelte state, props, or any shared data, sort a copy - toSorted() (ES2023) or [...arr].sort(...). For multi-key ordering (price, then name), chain comparisons with || inside one comparator rather than sorting twice, and rely on the guaranteed stability of modern sort() to preserve equal-key order.
⚠️ Common Mistakes
Sorting numbers without a comparator: [10, 2, 1].sort() returns [1, 10, 2] because the spec stringifies elements and compares code units, so "10" sorts before "2". The bug hides in test data like [1, 2, 3] where string order coincides with numeric order, then corrupts real data containing double-digit values. Numeric sorts always need (a, b) => a - b.
Sorting shared data in place. sort() mutates the array AND returns the same reference, so const sorted = data.sort(...) leaves you with two names for one reordered array - and if data was React state or a prop, the framework never learns it changed. Use toSorted() or copy first when anyone else can see the array.
Writing an inconsistent comparator - one that returns a boolean like a > b, or violates transitivity. Booleans coerce to 1 and 0, so "less than" information is never conveyed and the result is subtly misordered. The spec demands negative/zero/positive with consistent, transitive answers; break that contract and the output order is implementation-defined garbage.
Subtracting non-numbers in the comparator: (a, b) => a.date - b.date works only if date is a number or Date (which coerces via valueOf); with ISO strings it yields NaN for every pair, and a comparator that always returns NaN leaves the array in arbitrary order without any error being thrown.
Forgetting that undefined elements are special-cased: the spec sorts all undefined values to the END of the array without ever calling your comparator on them. Code expecting the comparator to place undefineds elsewhere - or to see them at all - behaves mysteriously; filter them out first if they need different treatment.
✅ Best Practices
Make (a, b) => a - b muscle memory for ascending numbers and b - a for descending, but switch to explicit comparisons for values that can be huge or non-finite - subtraction can overflow to Infinity minus Infinity (NaN) with extreme values, whereas (a < b ? -1 : a > b ? 1 : 0) is always safe.
Sort user-visible strings with localeCompare() or, for repeated sorts, a cached Intl.Collator: collator.compare handles accents, case, and language rules that code-unit comparison butchers, and new Intl.Collator(locale, { numeric: true }) even sorts "item2" before "item10" the way humans expect.
Prefer toSorted() wherever ES2023 is available - identical comparator semantics, fresh array returned, source untouched. It eliminates the entire "who else references this array" analysis that in-place sort() forces on every reviewer.
Express multi-key ordering as one comparator chained with ||: (a, b) => a.price - b.price || a.name.localeCompare(b.name). Each clause returns 0 for ties, letting the next key break them - clearer and faster than sorting the array multiple times.
When the sort key is expensive to compute (parsing dates, normalizing strings), decorate first: map each element to [key, element], sort by the precomputed key, then unwrap. Comparators run O(n log n) times, so computing the key once per element instead of once per comparison is a real win on large arrays.
⚡ Performance Notes
V8 implements sort() with TimSort (since V8 7.0 / Chrome 70): O(n log n) worst case, O(n) on already-sorted or mostly-sorted input, and stable - the ECMAScript spec has required stability since ES2019, so equal elements keep their relative order in all modern engines. The dominant cost in practice is the comparator, which runs roughly n log n times as a JavaScript function call; keeping it tiny and monomorphic lets V8 inline it, while allocating objects or parsing dates inside it multiplies the total work enormously (precompute such keys first). localeCompare() is significantly more expensive than code-unit comparison, so cache an Intl.Collator and pass collator.compare for large localized sorts. Sorting 10,000 mixed records takes on the order of a millisecond; re-sorting on every render or keystroke, not the sort itself, is what shows up in profiles - memoize sorted views and re-sort only when data or sort key changes. toSorted() adds one O(n) copy on top.
🌍 Real World Example
Sorting Products by Multiple Criteria
Real product listings almost never sort by a single key - equal prices need a deterministic tiebreak or the order shifts confusingly between renders. This comparator encodes "price ascending, then name alphabetically" in the standard chained form: the numeric difference handles the primary key, and only when it is exactly 0 does control fall through to localeCompare() for the secondary key. Spreading into a copy before sorting keeps the source array authoritative, which is what you want when the same data feeds multiple differently-sorted views. The identical comparator drops into toSorted() unchanged once ES2023 is your baseline. The || chaining pattern extends naturally to three or more keys.
const products = [
{name: 'Banana', price: 1.50},
{name: 'Apple', price: 1.50},
{name: 'Cherry', price: 2.00}
];
// Sort by price, then by name
const sorted = [...products].sort((a, b) => {
if (a.price !== b.price) {
return a.price - b.price; // Price ascending
}
return a.name.localeCompare(b.name); // Name alphabetically
});
// [{name: 'Apple', price: 1.50}, {name: 'Banana', price: 1.50}, {name: 'Cherry', price: 2.00}]