Back to KB
Difficulty
Intermediate
Read Time
7 min

JavaScript .sort(), "Why b - a, Not a - b"

By Codcompass TeamΒ·Β·7 min read

Mastering JavaScript Array Sorting: Comparator Contracts, Immutability, and Production Patterns

Current Situation Analysis

JavaScript's Array.prototype.sort() is frequently treated as a trivial utility, yet it remains one of the most common sources of silent runtime defects in production applications. The core issue stems from a mismatch between developer expectations and the engine's actual contract. Most engineers assume sort() behaves like a mathematical ordering function, but the JavaScript specification delegates all ordering logic to a user-provided comparator. When that comparator is omitted or misimplemented, the engine falls back to lexicographic string conversion, producing counterintuitive results that only surface during data rendering or API payload generation.

This problem is systematically overlooked for three reasons:

  1. Implicit Type Coercion: The default behavior converts elements to UTF-16 strings before comparison. Numeric arrays like [10, 2, 100] become ["10", "2", "100"], sorting alphabetically rather than mathematically. This passes linting and unit tests if test data isn't carefully constructed.
  2. Mutation Side-Effects: sort() operates in-place. In state-driven architectures (React, Vue, Redux, Pinia), mutating the source array breaks referential equality checks, causing unnecessary re-renders or stale UI states.
  3. Sign-Based Routing Misunderstanding: The comparator contract relies exclusively on the mathematical sign of the return value, not its magnitude. Developers frequently return booleans, strings, or inconsistent values, trusting the engine to "figure it out." V8 and SpiderMonkey will not throw; they will silently produce unstable or incorrect orderings.

Industry telemetry from static analysis tools and runtime error tracking platforms consistently flags improper comparators as a top-tier array manipulation defect. The lack of compile-time type enforcement in JavaScript means the engine assumes the developer understands the contract. When that assumption fails, debugging requires tracing execution paths through internal sorting algorithms (typically Timsort or QuickSort variants), which are opaque by design.

WOW Moment: Key Findings

The critical insight that separates fragile sorting logic from production-ready implementations is recognizing that the comparator's return value is a directional signal, not a magnitude metric. The engine only cares about whether the result is negative, zero, or positive. This decouples sorting logic from data representation and enables predictable ordering across types.

ApproachOrdering AccuracyMemory FootprintFramework CompatibilityExecution Speed
Default .sort()Low (lexicographic)MinimalHighFast
Numeric Comparator (a,b)=>a-bHigh (mathematical)MinimalHighFast
Immutable .toSorted()HighElevated (copy)Very HighModerate
Locale-Aware String SortHigh (collation)MinimalHighModerate

Why this matters: Understanding the sign-based contract allows developers to abstract sorting logic into reusable, type-safe utilities. It also clarifies why subtraction works for numbers, why localeCompare is mandatory for strings, and why immutability must be explicitly managed. The table above demonstrates that

πŸŽ‰ Mid-Year Sale β€” Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register β€” Start Free Trial

7-day free trial Β· Cancel anytime Β· 30-day money-back